This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

Does the Sleepy End device keep its RX antenna on if polling times are too long?

Hello,

I have been trying to debug a curious behavior leading to higher energy consumption (~17mA) on my Sleepy End Device (Rx is off when idle).

I am using the nRF SDK for Zigbee v4.1, and the device is running both Bluetooth and Zigbee stacks.

I reimplemented zb_nrf52_sleep and zb_wake_up: those are only changing the state of the RNG. When I receive the Zigbee signal 15 (ZB_COMMON_SIGNAL_CAN_SLEEP), I set up a timer (app_timer_start()) to wake up in sleep_tmo milliseconds, during that time, the Zigbee event handler (zboss_main_loop_iteration) is not called.

It works well after the device joined the network (polling times are less than a second) but at some point, the polling time increases to 4254ms. After that, the power consumption is around 17mA. It seemed to me like the RX wasn't turned off and I thought maybe I am missing a packet, so I tried to reactive the RX 500ms earlier than sleep_tmo:

app_timer_start(m_zb_timer, APP_TIMER_TICKS((sleep_tmo > 4000 ? sleep_tmo - 500 : sleep_tmo)), NULL);

The first observation is that I can see there are 2 signals (15) received:

...
13:50:37.063 [0000001765:0:zigbee_manager.c:78] 261 <-- sleep_tmo
13:50:38.047 [0000001766:0:zigbee_manager.c:110] ZB signal (15, status: 0)
13:50:38.052 [0000001766:0:zigbee_manager.c:78] 721
13:50:39.059 [0000001767:0:zigbee_manager.c:110] ZB signal (15, status: 0)
13:50:39.063 [0000001767:0:zigbee_manager.c:78] 4254 <-- sleep_tmo increased
13:50:43.043 [0000001770:1:zigbee_manager.c:110] ZB signal (15, status: 0) <-- first signal, doesn't seem to make the device go back to sleep
13:50:43.051 [0000001771:0:zigbee_manager.c:110] ZB signal (15, status: 0) <-- second signal
13:50:43.056 [0000001771:0:zigbee_manager.c:78] 4992 <-- back to sleep

The second observation is that this time, the power consumption did not increase and I can keep it low (<200uA on average).

It seems like 500ms earlier is the minimum value I can use, otherwise, the trick does not work.

Does someone know how should I deal with "long" polling times?

Best,
Cyril

Parents
  •  Hi,

    Sorry for the late reply.

    I reimplemented zb_nrf52_sleep and zb_wake_up: those are only changing the state of the RNG. When I receive the Zigbee signal 15 (ZB_COMMON_SIGNAL_CAN_SLEEP), I set up a timer (app_timer_start()) to wake up in sleep_tmo milliseconds, during that time, the Zigbee event handler (zboss_main_loop_iteration) is not called.

    I would like to see how you are implementing this. I am not sure I quite understand what you are doing. Here are some of my thoughts about implementing power savings in sleepy end devices using multiprotocol Zigbee/BLE:

    First you can take a look a the BLE UART and Zigbee light switch example under the multiprotocol examples folder: https://infocenter.nordicsemi.com/topic/sdk_tz_v4.1.0/zigbee_multi_dynamic_light_switch_nus_example.html

    See sleepy_device_setup() and you can modify it to call zb_set_rx_on_when_idle(ZB_FALSE); to enable sleepy end device behaviour by default. This example also demonstrate how an additional way of reducing current consumption when the sleepy behaviour is enable by powering down RAM sections.

    When sleepy end device behaviour is enabled (RxOnWhenIdle is set to 'false') the ZB_COMMON_SIGNAL_CAN_SLEEP signal will be sent to the application anytime the scheduler finds out that the application can go to sleep, read more about this in 'Power saving for ZED'. You need to handle this signal to suspend any tasks and then go to sleep. zb_sleep_now() must be called from the ZB_COMMON_SIGNAL_CAN_SLEEP signal.

    If you call zb_sleep_now() the stack will call zb_nrf52_sleep(). See external/zboss/osif/zb_nrf52_common.c this function is implemented as a weak function, so you can reimplement it if your want your own going-to-deep-sleep policy without changing common components. See how this functions is implemented in the BLE/Zigbee UART light switch example:

    /**@brief Function which tries to put the device into deep sleep mode, caused by an empty Zigbee stack scheduler queue.
     *
     * Function is defined as weak; to be redefined if someone wants to implement their own
     * going-to-deep-sleep policy.
     */
    __WEAK zb_uint32_t zb_nrf52_sleep(zb_uint32_t sleep_tmo)
    {
      zb_uint32_t time_slept_ms = 0;
    
      if (!sleep_tmo)
      {
        return sleep_tmo;
      }
    
    #if (ZIGBEE_TRACE_LEVEL != 0)
      /* In case of trace libraries - the Zigbee stack may generate logs on each loop iteration, resulting in immediate
       * return from zb_osif_wait_for_event() each time. In such case, Zigbee stack should not be forced to
       * increment counters. Such solution may break the internal logic of the stack.
       */
      ZVUNUSED(time_slept_ms);
      return ZB_SLEEP_INVALID_VALUE;
    #else
    
      /* Disable Zigbee stack-related peripherals to save energy. */
      zb_nrf52_priph_disable();
    
      /* Schedule an RTC timer interrupt to sleep for no longer than sleep_tmo. */
      zb_nrf52_sched_sleep(sleep_tmo);
    
      /* Wait for an event. */
      zb_osif_wait_for_event();
    
      /* Get RTC timer value to obtain sleep time. */
      time_slept_ms = zb_nrf52_get_time_slept();
    
      /* Enable Zigbee stack-related peripherals. */
      zb_nrf52_priph_enable();
    
      return time_slept_ms;
    #endif
    }
    

    Can you share your implementation of zb_nrf52_sleep() and zb_wake_up()? And where are you calling zb_sleep_now()?

    Best regards,

    Marjeris

  • Hello,

    I did everything you advised me to do:

    void
    zb_wake_up(void *p_context)
    {
        /* Restore the Random generator */
        ret_code_t err_code = nrf_drv_rng_init(NULL);
        APP_ERROR_CHECK(err_code);
    
        m_zb_sleeping = false;
    }
    
    /// Put everything related to Zigbee in suspend and re-enable ZB queue processing after `sleep_tmo`
    /// This function is called from zb_sleep_now(), see zigbee_default_signal_handler()
    /// Overrides __weak implementation in zb_nrf52_timer.c
    /// This feature is activated if zb_set_rx_on_when_idle(ZB_FALSE) is called.
    /// \param sleep_tmo Time to sleep in ms
    /// \return
    zb_uint32_t
    zb_nrf52_sleep(zb_uint32_t sleep_tmo)
    {
        if (!sleep_tmo)
        {
            return sleep_tmo;
        }
    
        /* Previously deinitialise Random generator */
        nrf_drv_rng_uninit();
    
        // disable ZB event handling by setting the sleeping flag
        m_zb_sleeping = true;
        uint32_t sleep_ms = sleep_tmo;
    
        if (sleep_ms > ZB_MAX_LONG_POLL_INTERVAL_MS)
        {
            sleep_ms = ZB_MAX_LONG_POLL_INTERVAL_MS;
        }
        else if (sleep_tmo > 4000)
        {
            sleep_ms = sleep_ms - 500;
        }
    
        SC_LOG_INFO("Sleeping %lums (was %u)", sleep_ms, sleep_tmo);
    
        // we are not going to handle ZB events until sleep_tmo
        app_timer_start(m_zb_timer, APP_TIMER_TICKS(sleep_ms), NULL);
    
        return sleep_tmo;
    }
    
    // this is called from my main loop
    void
    zigbee_handle_events()
    {
        if (!m_zb_sleeping)
        {
            zboss_main_loop_iteration();
        }
    }
    
    /**@brief Zigbee stack event handler.
     *
     * @param[in]   bufid   Reference to the Zigbee stack buffer used to pass signal.
     */
    void
    zboss_signal_handler(zb_bufid_t bufid)
    {
        zb_zdo_app_signal_type_t sig = zb_get_app_signal(bufid, NULL);
        zb_ret_t status = ZB_GET_APP_SIGNAL_STATUS(bufid);
    
        SC_LOG_INFO("ZB signal (%u, status: %d)", sig, status);
        UNUSED_VARIABLE(status);
        UNUSED_VARIABLE(sig);
    
        // default signal handling
        zb_ret_t err_code = zigbee_default_signal_handler(bufid);
        ZB_ERROR_CHECK(err_code);
    
        if (status == RET_OK)
        {
            switch (sig)
            {
                case ZB_BDB_SIGNAL_DEVICE_REBOOT:
                    zb_zdo_pim_set_long_poll_interval(ZB_MAX_LONG_POLL_INTERVAL_MS);
                    break;
    
                default:break;
            }
        }
    
        if (bufid)
        {
            zb_buf_free(bufid);
        }
    }
    

    But at some point, the current consumption rises to 17mA if I don't wake up the stack a bit before `sleep_tmo` (actually, only when sleep_tmo is longer than 4000ms)

  • Yes, I am using the PPK. I could send a screenshot on Monday or Tuesday.

    17mA is the average current once the sleeping time is more than 4000ms, before that or if I wake up the Zigbee peripheral a bit earlier, the average current is below 200uA.

  • Hi,

    Yes, please send the PPK measurements when they are available and steps to reproduce the issue so our developers can take a look at it.

    Best regards,

    Marjeris

  • Hello

    It's been a while but I still have the issue so here is below the screenshot of the PPK measurements.

    I am setting a polling time of 5 seconds (ZB_MAX_LONG_POLL_INTERVAL_MS 5000)

    Below is the weird behavior I am witnessing: whenever the sleeping time is more than ~4000ms, the RX antenna seems to be left on. (at the beginning, we can see some BLE advertising as well). I am using the UART to print the sleeping time on the right.

    Then, I apply my dirty fix:

    /// Put everything related to Zigbee in suspend and re-enable ZB queue processing after \c sleep_tmo
    /// This function is called from zb_sleep_now(), see zigbee_default_signal_handler()
    /// Overrides __weak implementation in zb_nrf52_timer.c
    /// This feature is activated if zb_set_rx_on_when_idle(ZB_FALSE) is called.
    /// \param sleep_tmo Time to sleep in ms
    /// \return
    zb_uint32_t
    zb_nrf52_sleep(zb_uint32_t sleep_tmo)
    {
        if (!sleep_tmo)
        {
            return sleep_tmo;
        }
    
        // Previously deinitialise Random generator
        nrf_drv_rng_uninit();
    
        // disable ZB event handling by setting the sleeping flag
        m_zb_sleeping = true;
    
        if (sleep_tmo > 4000)
        {
            sleep_tmo = sleep_tmo - 500;
        }
    
        SC_LOG_INFO("ZB sleeping: %ums", sleep_tmo);
    
        // we are not going to handle ZB events until sleep_tmo, minus 1
        // not sure we need to remove 1
        app_timer_start(m_zb_timer, APP_TIMER_TICKS(sleep_tmo), NULL);
    
        return sleep_tmo;
    }

    Below, is the current consumption with the fix:

    Best,

    Cyril

  • while writing the last comment I thought about the returned value as well and changed only that value (not the timer):

    return sleep_tmo-500;

    And it actually works: current consumption is kept low.

    So the question is how do you use that value?

    I hope this will help you solve that issue.

    Best,

  • Actually, removing 1 also works as a workaround.

    return sleep_tmo-1;

    When removing 500 like in my last comment, I can see that the current is a bit higher during 500ms when getting out of sleep.


    I might not have totally understood the goal of that returned value?

Reply Children
Related