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.

CC2340R5: Demo code for zb_nvram_write_dataset

Part Number: CC2340R5
Other Parts Discussed in Thread: SYSCONFIG

Tool/software:

I am having trouble making zb_nvram_write_dataset work in ZBOSS. I followed ZBOSS user's guide as well as some demo code in Nordic's document to create callbacks for NVRAM and register the callback.

https://docs.nordicsemi.com/bundle/nrf5_sdk_thread_zigbee/page/group_zboss_nvram.html

However, the program does not return after zb_osif_nvram_write() is called in the write callback.

Please find my on_off_switch.c as below:

/******************************************************************************
 Group: CMCU LPRF
 Target Device: cc23xx

 ******************************************************************************
 
 Copyright (c) 2024-2025, Texas Instruments Incorporated
 All rights reserved.

 Redistribution and use in source and binary forms, with or without
 modification, are permitted provided that the following conditions
 are met:

 *  Redistributions of source code must retain the above copyright
    notice, this list of conditions and the following disclaimer.

 *  Redistributions in binary form 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.

 *  Neither the name of Texas Instruments Incorporated nor the names of
    its contributors may be used to endorse or promote products derived
    from this software without specific prior written permission.

 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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.

 ******************************************************************************
 
 
 *****************************************************************************/

/***** Trace related defines *****/
#define ZB_TRACE_FILE_ID 40124

/****** Application defines ******/
#define ZB_SWITCH_ENDPOINT          10

#include <ti/log/Log.h>

#include "ti_zigbee_config.h"
#include "zboss_api.h"
#include "zb_led_button.h"

#include <ti/devices/DeviceFamily.h>
#include DeviceFamily_constructPath(inc/hw_fcfg.h)
#include DeviceFamily_constructPath(inc/hw_memmap.h)

/* for button handling */
#include <ti/drivers/GPIO.h>
#include "ti_drivers_config.h"

#ifdef ZB_CONFIGURABLE_MEM
#include "zb_mem_config_lprf3.h"
#endif

#if !defined ZB_ED_FUNC
#error define ZB_ED_ROLE to compile the tests
#endif

/****** Application variables declarations ******/
/* IEEE address of the device */
zb_bool_t cmd_in_progress = ZB_FALSE;
zb_bool_t perform_factory_reset = ZB_FALSE;

/****** Application function declarations ******/
zb_uint8_t zcl_specific_cluster_cmd_handler(zb_uint8_t param);
void on_off_read_attr_resp_handler(zb_bufid_t cmd_buf);
void send_toggle_req(zb_uint8_t param);
void button_press_handler(zb_uint8_t param);

/****** Cluster declarations ******/
/* Switch config cluster attributes */
zb_uint8_t attr_switch_type =
    ZB_ZCL_ON_OFF_SWITCH_CONFIGURATION_SWITCH_TYPE_TOGGLE;
zb_uint8_t attr_switch_actions =
    ZB_ZCL_ON_OFF_SWITCH_CONFIGURATION_SWITCH_ACTIONS_DEFAULT_VALUE;

ZB_ZCL_DECLARE_ON_OFF_SWITCH_CONFIGURATION_ATTRIB_LIST(switch_cfg_attr_list,
                                                       &attr_switch_type,
                                                       &attr_switch_actions);
/* Basic cluster attributes */
zb_uint8_t attr_zcl_version  = ZB_ZCL_BASIC_ZCL_VERSION_DEFAULT_VALUE;
zb_uint8_t attr_power_source = ZB_ZCL_BASIC_POWER_SOURCE_DEFAULT_VALUE;
ZB_ZCL_DECLARE_BASIC_ATTRIB_LIST(basic_attr_list, &attr_zcl_version, &attr_power_source);
/* Identify cluster attributes */
zb_uint16_t attr_identify_time = 0;
ZB_ZCL_DECLARE_IDENTIFY_ATTRIB_LIST(identify_attr_list, &attr_identify_time);
/* Declare cluster list for the device */
ZB_HA_DECLARE_ON_OFF_SWITCH_CLUSTER_LIST(on_off_switch_clusters,
                                         switch_cfg_attr_list,
                                         basic_attr_list,
                                         identify_attr_list);
/* Declare endpoint */
ZB_HA_DECLARE_ON_OFF_SWITCH_EP(on_off_switch_ep, ZB_SWITCH_ENDPOINT, on_off_switch_clusters);
/* Declare application's device context for single-endpoint device */
ZB_HA_DECLARE_ON_OFF_SWITCH_CTX(on_off_switch_ctx, on_off_switch_ep);

void my_main_loop()
{
  while (1)
  {
    /* ... User code ... */
    zboss_main_loop_iteration();
    /* ... User code ... */
  }
}

void module_zigbee_nvram_read_app_data(zb_uint8_t page, zb_uint32_t pos, zb_uint16_t payload_length)
{
  zb_uint8_t buf[1] = {0};
  zb_osif_nvram_read(page, pos, buf, payload_length);
  Log_printf(LogModule_Zigbee_App, Log_INFO, "module_zigbee_nvram_read_app_data, buf[0] = 0x%x", buf[0]);
}

zb_uint8_t writeBuf[1] = {0x55};
zb_ret_t module_zigbee_nvram_write_app_data(zb_uint8_t page, zb_uint32_t pos)
{
  zb_ret_t ret;
//  zb_uint8_t buf[1] = {0x55};
  zb_uint16_t len = 1;
  ret = zb_osif_nvram_write(page, pos, writeBuf, len);
  Log_printf(LogModule_Zigbee_App, Log_INFO, "module_zigbee_nvram_write_app_data, ret = %d", ret);
  return ret;
}

zb_uint16_t module_get_nvram_data_size(void)
{
  return 1;
}

MAIN()
{
  ARGV_UNUSED;

  /* Global ZBOSS initialization */
  ZB_INIT("on_off_switch");

  #ifdef ZB_LONG_ADDR
  // use the address that the customer set in the pre-defined symbols tab
  zb_ieee_addr_t g_long_addr = ZB_LONG_ADDR;
  zb_set_long_address(g_long_addr);
  #else
  /* Set the device's long address to the IEEE address pulling from the FCFG of the device */
  zb_ieee_addr_t ieee_mac_addr;
  ZB_MEMCPY(ieee_mac_addr, fcfg->deviceInfo.macAddr, 8);
  zb_set_long_address(ieee_mac_addr);
  #endif // ZB_LONG_ADDR

#ifdef ZB_COORDINATOR_ROLE
  zb_set_network_coordinator_role(DEFAULT_CHANLIST);
#ifdef DEFAULT_NWK_KEY
  zb_uint8_t nwk_key[16] = DEFAULT_NWK_KEY;
  zb_secur_setup_nwk_key(nwk_key, 0);
#endif //DEFAULT_NWK_KEY
  zb_set_max_children(MAX_CHILDREN);

#elif defined ZB_ROUTER_ROLE && !defined ZB_COORDINATOR_ROLE
  zb_set_network_router_role(DEFAULT_CHANLIST);
  zb_set_max_children(MAX_CHILDREN);

#elif defined ZB_ED_ROLE
  zb_set_network_ed_role(DEFAULT_CHANLIST);

  /* Set end-device configuration parameters */
  zb_set_ed_timeout(ED_TIMEOUT_VALUE);
  zb_set_rx_on_when_idle(ED_RX_ALWAYS_ON);
#if ( ED_RX_ALWAYS_ON == ZB_FALSE )
  zb_set_keepalive_timeout(ZB_MILLISECONDS_TO_BEACON_INTERVAL(ED_POLL_RATE));
#ifdef DISABLE_TURBO_POLL
  // Disable turbo poll feature
  zb_zdo_pim_permit_turbo_poll(ZB_FALSE);
  zb_zdo_pim_set_long_poll_interval(ED_POLL_RATE);
#endif // DISABLE_TURBO_POLL
#endif // ED_RX_ALWAYS_ON
#endif //ZB_ED_ROLE

  zb_set_nvram_erase_at_start(ZB_FALSE);

  /* Register application callback for reading application data from NVRAM */
  zb_nvram_register_app1_read_cb(module_zigbee_nvram_read_app_data);
  /* Register application callback for writing application data to NVRAM */
  zb_nvram_register_app1_write_cb(module_zigbee_nvram_write_app_data, module_get_nvram_data_size);

//  zb_nvram_write_dataset(ZB_NVRAM_APP_DATA1);
//  Log_printf(LogModule_Zigbee_App, Log_INFO, "write ZB_NVRAM_APP_DATA1");

  /* Register device ZCL context */
  ZB_AF_REGISTER_DEVICE_CTX(&on_off_switch_ctx);
  /* Register cluster commands handler for a specific endpoint */
  ZB_AF_SET_ENDPOINT_HANDLER(ZB_SWITCH_ENDPOINT, zcl_specific_cluster_cmd_handler);

  /* Initiate the stack start without starting the commissioning */
  if (zboss_start_no_autostart() != RET_OK)
  {
    Log_printf(LogModule_Zigbee_App, Log_ERROR, "zboss_start failed");
  }
  else
  {
    GPIO_setConfig(CONFIG_GPIO_BTN1, GPIO_CFG_IN_PU);
    GPIO_setConfig(CONFIG_GPIO_BTN2, GPIO_CFG_IN_PU);
    // if either button 1 or button 2 gets pressed
    zb_bool_t sideButtonPressed = ((GPIO_read((zb_uint8_t)CONFIG_GPIO_BTN1) == 0U) || (GPIO_read((zb_uint8_t)CONFIG_GPIO_BTN2) == 0U));
    // then perform a factory reset
    if (sideButtonPressed)
    {
      perform_factory_reset = ZB_TRUE;
      Log_printf(LogModule_Zigbee_App, Log_INFO, "perform factory reset");
    }
    /* Call the application-specific main loop */
    my_main_loop();
  }

  MAIN_RETURN(0);
}

static zb_bool_t finding_binding_cb(zb_int16_t status,
                                    zb_ieee_addr_t addr,
                                    zb_uint8_t ep,
                                    zb_uint16_t cluster)
{
  /* Unused without trace. */
  ZVUNUSED(status);
  ZVUNUSED(addr);
  ZVUNUSED(ep);
  ZVUNUSED(cluster);

  Log_printf(LogModule_Zigbee_App, Log_INFO, "finding_binding_cb status %d addr %x ep %d cluster %d",
             status, ((zb_uint32_t *)addr)[0], ep, cluster);
  return ZB_TRUE;
}

zb_uint8_t zcl_specific_cluster_cmd_handler(zb_uint8_t param)
{
  zb_zcl_parsed_hdr_t *cmd_info = ZB_BUF_GET_PARAM(param, zb_zcl_parsed_hdr_t);
  zb_bool_t unknown_cmd_received = ZB_TRUE;

  Log_printf(LogModule_Zigbee_App, Log_INFO, "> zcl_specific_cluster_cmd_handler %i", param);
  Log_printf(LogModule_Zigbee_App, Log_INFO, "payload size: %i", zb_buf_len(param));

  if (cmd_info->cmd_direction == ZB_ZCL_FRAME_DIRECTION_TO_CLI)
  {
    if (cmd_info->cmd_id == ZB_ZCL_CMD_DEFAULT_RESP)
    {
      unknown_cmd_received = ZB_FALSE;

      cmd_in_progress = ZB_FALSE;

      zb_buf_free(param);
    }
  }

  Log_printf(LogModule_Zigbee_App, Log_INFO, "< zcl_specific_cluster_cmd_handler %i", param);
  return ! unknown_cmd_received;
}

void send_toggle_req(zb_uint8_t param)
{
  zb_uint16_t addr = 0;

  ZB_ASSERT(param);

  if (ZB_JOINED()  && !cmd_in_progress)
  {
    cmd_in_progress = ZB_TRUE;
    Log_printf(LogModule_Zigbee_App, Log_INFO, "send_toggle_req %d - send toggle", param);

    /* Destination address and endpoint are unknown; command will be sent via binding */
    ZB_ZCL_ON_OFF_SEND_TOGGLE_REQ(
      param,
      addr,
      ZB_APS_ADDR_MODE_DST_ADDR_ENDP_NOT_PRESENT,
      0,
      ZB_SWITCH_ENDPOINT,
      ZB_AF_HA_PROFILE_ID,
      ZB_FALSE, NULL);
  }
  else
  {
    Log_printf(LogModule_Zigbee_App, Log_INFO, "send_toggle_req %d - not joined", param);
    zb_buf_free(param);
  }
}

void button_press_handler(zb_uint8_t param)
{
  if (!param)
  {
    /* Button is pressed, gets buffer for outgoing command */
    zb_buf_get_out_delayed(button_press_handler);
  }
  else
  {
    Log_printf(LogModule_Zigbee_App, Log_INFO, "button_press_handler %d", param);
    send_toggle_req(param);
#ifndef ZB_USE_BUTTONS
    /* Do not have buttons in simulator - just start periodic on/off sending */
    ZB_SCHEDULE_APP_ALARM(button_press_handler, 0, 7 * ZB_TIME_ONE_SECOND);
#endif
  }
}

void permit_joining_cb(zb_uint8_t param)
{
  Log_printf(LogModule_Zigbee_App, Log_INFO, "permit joining done");
  zb_buf_free(param);
}

void start_finding_binding(zb_uint8_t param)
{
  ZVUNUSED(param);

  Log_printf(LogModule_Zigbee_App, Log_INFO, "Successful steering, start f&b initiator");
  zb_bdb_finding_binding_initiator(ZB_SWITCH_ENDPOINT, finding_binding_cb);
}

void zboss_signal_handler(zb_uint8_t param)
{
  zb_zdo_app_signal_hdr_t *sg_p = NULL;
  zb_zdo_app_signal_type_t sig = zb_get_app_signal(param, &sg_p);
  zb_bufid_t buf;
  zb_bufid_t req_buf = 0;
  zb_zdo_mgmt_permit_joining_req_param_t *req_param;

#ifdef ZB_USE_BUTTONS
  /* Now register handlers for buttons */
  zb_int32_t i;
  for (i = 0; i < ZB_N_BUTTONS; ++i)
  {
    zb_button_register_handler(i, 0, button_press_handler);
  }
#endif

  if (ZB_GET_APP_SIGNAL_STATUS(param) == 0)
  {
    switch(sig)
    {
      case ZB_ZDO_SIGNAL_SKIP_STARTUP:
#ifndef ZB_MACSPLIT_HOST
        Log_printf(LogModule_Zigbee_App, Log_INFO, "ZB_ZDO_SIGNAL_SKIP_STARTUP: boot, not started yet");
        zboss_start_continue();
#endif /* ZB_MACSPLIT_HOST */
        break;

#ifdef ZB_MACSPLIT_HOST
      case ZB_MACSPLIT_DEVICE_BOOT:
        Log_printf(LogModule_Zigbee_App, Log_INFO, "ZB_MACSPLIT_DEVICE_BOOT: boot, not started yet");
        zboss_start_continue();
        break;
#endif /* ZB_MACSPLIT_HOST */
      case ZB_BDB_SIGNAL_DEVICE_FIRST_START:
        Log_printf(LogModule_Zigbee_App, Log_INFO, "FIRST_START: start steering");
        if (perform_factory_reset)
        {
          // passing in 0 as the parameter means that a buffer will be allocated automatically for the reset
          zb_bdb_reset_via_local_action(0);
          perform_factory_reset = ZB_FALSE;
        }
        bdb_start_top_level_commissioning(ZB_BDB_NETWORK_STEERING);

        buf = zb_buf_get_out();
        if (!buf)
        {
          Log_printf(LogModule_Zigbee_App, Log_WARNING, "no buffer available");
          break;
        }

        req_param = ZB_BUF_GET_PARAM(buf, zb_zdo_mgmt_permit_joining_req_param_t);

        req_param->dest_addr = 0xfffc;
        req_param->permit_duration = 0;
        req_param->tc_significance = 1;

        zb_zdo_mgmt_permit_joining_req(buf, permit_joining_cb);

        break;
      case ZB_BDB_SIGNAL_DEVICE_REBOOT:
        Log_printf(LogModule_Zigbee_App, Log_INFO, "Device RESTARTED OK");
        if (perform_factory_reset)
        {
          Log_printf(LogModule_Zigbee_App, Log_INFO, "Performing a factory reset.");
          zb_bdb_reset_via_local_action(0);
          perform_factory_reset = ZB_FALSE;
        }
#ifndef ZB_USE_BUTTONS
        /* Do not have buttons in simulator - just start periodic on/off sending */
        cmd_in_progress = ZB_FALSE;
        ZB_SCHEDULE_APP_ALARM_CANCEL(button_press_handler, ZB_ALARM_ANY_PARAM);
        ZB_SCHEDULE_APP_ALARM(button_press_handler, 0, 7 * ZB_TIME_ONE_SECOND);
#endif
        break;
#ifdef ZB_COORDINATOR_ROLE
      case ZB_ZDO_SIGNAL_DEVICE_ANNCE:
#else
      case ZB_BDB_SIGNAL_TC_REJOIN_DONE:
        Log_printf(LogModule_Zigbee_App, Log_INFO, "TC rejoin is completed successfully");
      case ZB_BDB_SIGNAL_STEERING:
#endif
      {
        zb_nwk_device_type_t device_type = ZB_NWK_DEVICE_TYPE_NONE;
        device_type = zb_get_device_type();
        ZVUNUSED(device_type);
        Log_printf(LogModule_Zigbee_App, Log_INFO, "Device (%d) STARTED OK", device_type);
        ZB_SCHEDULE_APP_ALARM(start_finding_binding, 0, 3 * ZB_TIME_ONE_SECOND);
        break;
      }

      case ZB_BDB_SIGNAL_FINDING_AND_BINDING_INITIATOR_FINISHED:
      {
        Log_printf(LogModule_Zigbee_App, Log_INFO, "Finding&binding done");
#ifndef ZB_USE_BUTTONS
        /* Do not have buttons in simulator - just start periodic on/off sending */
        cmd_in_progress = ZB_FALSE;
        ZB_SCHEDULE_APP_ALARM_CANCEL(button_press_handler, ZB_ALARM_ANY_PARAM);
        ZB_SCHEDULE_APP_ALARM(button_press_handler, 0, 7 * ZB_TIME_ONE_SECOND);
#endif
      }
      break;

      case ZB_ZDO_SIGNAL_LEAVE:
      break;
      case ZB_COMMON_SIGNAL_CAN_SLEEP:
      {
#ifdef ZB_USE_SLEEP
        zb_sleep_now();
#endif
        break;
      }
      case ZB_ZDO_SIGNAL_PRODUCTION_CONFIG_READY:
      {
        Log_printf(LogModule_Zigbee_App, Log_INFO, "Production config is ready");
        break;
      }

      default:
        Log_printf(LogModule_Zigbee_App, Log_WARNING, "Unknown signal %d, do nothing", sig);
    }
  }
  else
  {
    switch (sig)
    {
      case ZB_BDB_SIGNAL_DEVICE_FIRST_START:
        Log_printf(LogModule_Zigbee_App, Log_WARNING, "Device can not find any network on start, so try to perform network steering");
          zb_nvram_write_dataset(ZB_NVRAM_APP_DATA1);
          Log_printf(LogModule_Zigbee_App, Log_INFO, "write ZB_NVRAM_APP_DATA1");
        break; /* ZB_BDB_SIGNAL_DEVICE_FIRST_START */

      case ZB_BDB_SIGNAL_DEVICE_REBOOT:
        Log_printf(LogModule_Zigbee_App, Log_WARNING, "Device can not find any network on restart");

        if (zb_bdb_is_factory_new())
        {
          /* Device tried to perform TC rejoin after reboot and lost its authentication flag.
           * Do nothing here and wait for ZB_BDB_SIGNAL_TC_REJOIN_DONE to handle TC rejoin error */
          Log_printf(LogModule_Zigbee_App, Log_WARNING, "Device lost authentication flag");
        }
        else
        {
          /* Device tried to perform secure rejoin, but didn't found any networks or can't decrypt Rejoin Response
           * (it is possible when Trust Center changes network key when ZED is powered off) */
          Log_printf(LogModule_Zigbee_App, Log_WARNING, "Device is still authenticated, try to perform TC rejoin");
          ZB_SCHEDULE_APP_ALARM(zb_bdb_initiate_tc_rejoin, 0, ZB_TIME_ONE_SECOND);
        }
        break; /* ZB_BDB_SIGNAL_DEVICE_REBOOT */

      case ZB_ZDO_SIGNAL_PRODUCTION_CONFIG_READY:
        Log_printf(LogModule_Zigbee_App, Log_INFO, "Production config is not present or invalid");
        break; /* ZB_ZDO_SIGNAL_PRODUCTION_CONFIG_READY */

      case ZB_BDB_SIGNAL_TC_REJOIN_DONE:
        Log_printf(LogModule_Zigbee_App, Log_WARNING, "TC rejoin failed, so try it again with interval");

        ZB_SCHEDULE_APP_ALARM(zb_bdb_initiate_tc_rejoin, 0, 3 * ZB_TIME_ONE_SECOND);
        break; /* ZB_BDB_SIGNAL_TC_REJOIN_DONE */

      default:
        Log_printf(LogModule_Zigbee_App, Log_WARNING, "Unknown signal %hd with error status, do nothing", sig);
        break;
    }
  }

  if (param)
  {
    zb_buf_free(param);
  }
}

Please help review the code for errors and it's better if there is a demo for NVRAM write and read.

Best regards,

Shuyang

  • Hi Shuyang,

    Thanks for sharing the details and code.

    We have similar documentation here: Developing with ZBOSS for Zigbee: NVRAM
    (Found from ZBOSS User's Guide).
    However, I do see potential concern that the Example code is not showing up in ours.

    When you say that "zb_osif_nvram_write" doesn't return, what do you see if you pause the debugger?
    After you notice the "no return" condition and paus debugger, can you share screenshots of:

    1. Callstack
    2. Disassembly
    3. C code

    Is the device joined to a network when you try it?

    If yes, is it still active in the network? (E.g. try to toggle a Zigbee light)

    Thanks,
    Toby

  • Hi Toby,

    Actually it is after zb_osif_nvram_write is executed inside the write callback, and the device resets at zb_nvram_write_dataset -> zb_nvram_write_end:

    The API is called before joining a network:

    My customer has tried to call the write API after joining a network, it is working but the read callback is not invoked after the next power up.

    Best regards,

    Shuyang

  • Hi Shuyang,

    Thanks for sharing the details.

    I'll take a look into it and provide an update within 3 business days.

    Thanks,
    Toby

  • Hi Shuyang,

    Please forgive my late entry and if any of the below therefore overlaps the information you've already provided.  TI advises usage of the group__zboss__nvram.html APIs.  The docs indicate that zboss provides a mechanism where:

    1. The application defines a structure/data it wants to store in NV
    2. The application provides a read/write callback to zboss
    3. On reset, if user nv data exists, the read callback is issued from zboss and the application can pull its data from nv
    4. A write to nv can be triggered calling zb_nvram_write_dataset() which will then trigger the write cb
    5. Once the write cb is executed the application can write its data to nv

    Here is a basic code execution snippet (note that this is pseudo-code and should only be used as reference):

      1. In MAIN():
          /* Register application callback for reading application data from NVRAM */
          zb_nvram_register_app1_read_cb(nvram_read_app_data);
          /* Register application callback for writing application data to NVRAM */
          zb_nvram_register_app1_write_cb(nvram_write_app_data, get_nvram_data_size);
        
      2. When determined by the application, like in a binding complete callback:
        (void)zb_nvram_write_dataset(ZB_NVRAM_APP_DATA1);
      3. NVRAM function examples:
        zb_uint16_t get_nvram_data_size(void)
        {
          return sizeof(device_nvram_dataset_t);
        }
        void nvram_read_app_data(zb_uint8_t page, zb_uint32_t pos, zb_uint16_t payload_length)
        {
          device_nvram_dataset_t ds;
          zb_ret_t ret;
          /* If we fail, trace is given and assertion is triggered */
          ret = zb_nvram_read_data(page, pos, (zb_uint8_t*)&ds, sizeof(ds));
          if (ret == RET_OK)
        {
            // ACTION TAKEN HERE
         }
        }
        zb_ret_t nvram_write_app_data(zb_uint8_t page, zb_uint32_t pos)
        {
          zb_ret_t ret;
          device_nvram_dataset_t ds;
          // ACTION TAKEN HERE
          /* If we fail, trace is given and assertion is triggered */
          ret = zb_nvram_write_data(page, pos, (zb_uint8_t*)&ds, sizeof(ds));
          return ret;
        }
        

      I recommend utilizing the most variables within the dataset structures to maximize on the available number of application data sets. Add variables per dataset as seen fit and keep in mind the “divisible by 4” rule.

      /* Application dataset for persisting into nvram */
      typedef ZB_PACKED_PRE struct device_nvram_dataset_s
      {
                        zb_uint8_t onoff_state;
         zb_uint8_t current_level;
          /* Size of the structure in bytes must be divisible by 4 */
          zb_uint8_t reserved[2];
      } ZB_PACKED_STRUCT
      device_nvram_dataset_t;
      

      After the zboss_start() call, the ZBOSS stack reads the whole flash page and looks for the valid version of each dataset. If the valid application dataset is found, the read/load callback is called. The callback should store the read value inside the RAM using its own static/global variable.  Afterwards, it is assumed that the application uses the static/global variable to perform changes or read dataset field values.

      If none of this is acceptable then you could simply allocate memory in SysConfig and the command linker file for internal NV application memory storage using the TI Driver example and API reference.

      Hope this helps,
      Ryan