Beware that this post is related to an SDK in maintenance mode
More Info: Consider nRF Connect SDK for new designs

Can't get BLE device to sleep

I have a custom PCBA with which has the flow of:

- Boot

- Advertise some temperature data read from TWI with a 5 second timeout using `ble_adv_fast_timeout`.

- I then set a timer to wake the `app_timer_start(m_awake_timer_id, APP_TIMER_TICKS(SLEEP_IN_MS), NULL);`

And I expect the device to go into sleep whilst its BLE is not advertising. Here's my main.c file:

#include "app_timer.h"
#include "nrf_pwr_mgmt.h"
#include "nrf_crypto_init.h"
#include "nrfx_clock.h"

#include "SEGGER_RTT.h"
#include "nrf_log.h"
#include "nrf_log_ctrl.h"
#include "nrf_log_default_backends.h"
#include "nrf_delay.h"

#include "customble.h"
#include "peripheral.h"
#include "encryption.h"

void timers_init(void)
{
  ret_code_t err_code = app_timer_init();
  APP_ERROR_CHECK(err_code);
}

static void idle_state_handle(void)
{
  if (NRF_LOG_PROCESS() == false)
  {
    nrf_pwr_mgmt_run();
  }
}

/**@brief Function for initializing power management.
 */
void power_management_init(void)
{
  ret_code_t err_code;
  err_code = nrf_pwr_mgmt_init();
  APP_ERROR_CHECK(err_code);
}

int main(void)
{
  NRF_LOG_INIT(NULL);
  NRF_LOG_DEFAULT_BACKENDS_INIT();

  ret_code_t reta = nrf_crypto_init();
  APP_ERROR_CHECK(reta);

  timers_init();
  ble_timers_init();
  ble_stack_init();

  power_management_init();

  gap_params_init();
  advertising_init();

  advertising_start();

  for (;;)
  {
    idle_state_handle();
  }
}

and here's the relevant sections of the customble.c code

void awake_timer_handler()
{
  NRF_LOG_INFO("Awake and start advertising");

  update_advertising_data_while_advertising();
  advertising_start();
}

void sleep_timer_handler()
{
  NRF_LOG_INFO("asleep");

  sd_ble_gap_adv_stop(m_advertising.adv_handle); // i've tried with and without this

  // Proceed with setting the device to low power mode
  app_timer_start(m_awake_timer_id, APP_TIMER_TICKS(SLEEP_IN_MS), NULL); // 60 seconds
}

void on_adv_evt(ble_adv_evt_t ble_adv_evt)
{
  NRF_LOG_INFO("BLE EVENT");
  switch (ble_adv_evt)
  {
  case BLE_ADV_EVT_IDLE:
    NRF_LOG_INFO("Advertising event: Idle");
    sleep_timer_handler();
    break;
  }
}

I have measured the amps on the device and during advertising it jumps to about 3mA and when it's not advertising (when I thought it would sleep) it's using ~1.48mA. What exactly am I doing wrong here?

I have commented lots of things out, and it's not the TWI causing the issue, it's definitely the BLE. When I comment out starting BLE, I get ~400uA constant.

Parents
  • Hello,

    3A sounds like a lot. I don't think you are able to even draw that much through the nRF. Not 1.48A either. We should be talking about perhaps 20mA, or something around those numbers.

    Have you had your PCB design through a design review with Nordic? If not, perhaps you should create a ticket for that (so that it will be assigned to one of our HW engineers).

    Other than that, I can't say that I can see anything wrong with your application. Regarding the power, I see that you are using idle_state_handle() in your main-loop, so the device should enter sleep.

    I don't know what kind of crypto things you are doing, but to rule it out. Does it still draw as much power if you remove that as well? (just to make sure that the CPU isn't running all the time). 

    How do you measure the current consumption? Are you using the Power Profiler Kit 2? Or some other device?

    Best regards,

    Edvin

  • Eugh. Apologies, of course I mean mA not A. so ~3mA and 1.4mA. I'll edit my original answer.

    I'm using both a custom PCBA and also an NRF52DK (by following the document to cut the connection then measure over the power consumption pins). I will take another reading using the DK but the power wasn't too different to what I was seeing on my custom PCBA (with no sleep happening).

    In terms of the crypto, it's not that, I've checked this also. It's definitely the BLE that's causing the device not to go into sleep mode.

  • Ah, that makes more sense then Slight smile

    Is it possible for me to test the application on a DK? Or if you are not able to share it as it is, a strip down version of the application that replicates the issue? Is there some other peripheral you are turning on when you enable BLE? You ruled out TWI. How about UART? Is that running? Perhaps for logging?

    And what advertising parameters are you using? Advertising interval, in particular.

    Best regards,

    Edvin

  • Absolutely, I'll get a stripped down demo uploaded here tomorrow.

    How about UART? Is that running? Perhaps for logging?

    I'll check these again but it seems like all my configuration has UART flags set to 0. For my test I disabled NRF_LOG_BACKEND_RTT_ENABLED. 

    > And what advertising parameters are you using? Advertising interval, in particular.

      init.advdata.name_type = BLE_ADVDATA_NO_NAME;
      init.advdata.include_ble_device_addr = false;
      init.advdata.include_appearance = false;
      init.advdata.flags = flags;
      init.advdata.p_manuf_specific_data = &manuf_specific_data;
      init.config.ble_adv_fast_enabled = true;
      init.config.ble_adv_fast_interval = NON_CONNECTABLE_ADV_INTERVAL;
      init.config.ble_adv_fast_timeout = 100;

  • prsboy said:

    I'll check these again but it seems like all my configuration has UART flags set to 0. For my test I disabled NRF_LOG_BACKEND_RTT_ENABLED. 

    NRF_LOG_BACKEND_RTT_ENABLED doesn't control the UART. It is possible to have both RTT and UART Logging at the same time. Just make sure you don't have NRF_LOG_BACKEND_UART_ENABLED set to 1. If you do, then the UART will be left on. 

    What is NON_CONNECTABLE_ADV_INTERVAL? 160 (100ms)?

    Best regards,

    Edvin

  • NRF_LOG_BACKEND_RTT_ENABLED doesn't control the UART. It is possible to have both RTT and UART Logging at the same time. Just make sure you don't have NRF_LOG_BACKEND_UART_ENABLED set to 1. If you do, then the UART will be left on. 

    Yes, I was under the belief that RTT could also cause the device not to sleep, so was just confirming that was also disabled during my tests. 

    I just checked and NRF_LOG_BACKEND_UART_ENABLED is indeed set to 0 so that's not the problem.

    > What is NON_CONNECTABLE_ADV_INTERVAL? 160 (100ms)?

    Yep, it's set to:

    #define NON_CONNECTABLE_ADV_INTERVAL MSEC_TO_UNITS(100, UNIT_0_625_MS)

  • I've attached the ZIP file, I don't have a device on me at the moment to verify it works but it's building successfully and should work. If you can't get it to run on the device I'll have to upload another ZIP in a few days when I'm back home.

    The path you can extract this to for all of the paths to align with the SDK:

    nRF5_SDK_17.1.0_ddde560/examples/ble_peripheral/ble_app_alert_notification

    output.zip

Reply
  • I've attached the ZIP file, I don't have a device on me at the moment to verify it works but it's building successfully and should work. If you can't get it to run on the device I'll have to upload another ZIP in a few days when I'm back home.

    The path you can extract this to for all of the paths to align with the SDK:

    nRF5_SDK_17.1.0_ddde560/examples/ble_peripheral/ble_app_alert_notification

    output.zip

Children
  • Thank you. I found it:

    Two things you need to change:

    1. In your preprocessor definitions, you need to remove DEVELOP_IN_NRF52832 if you are actually running on an nRF52810. If you are simulating the nRF52810 on an nRF52 DK, you should keep that definition. 

    2. In your sdk_config.h (this took some time to find, because you had your list on the top Slight smile), you need to set the LFCLK source to be either XTAL or the RC Oscillator. It was currently set to SYNTH, which will use the HFCLK to synthesize the LFCLK. This means that the HFCLK will be running at all times when you otherwise would use just the LFCLK. And the BLE stack enables the LFCLK, which was why you saw this behavior when enabling the softdevice.

    So remove DEVELOP_IN_NRF52832 if you are actually running the application on an nRF52810, and change the LFCLK source to either XTAL (if you have an XTAL) or RC_OSCILLATOR if you don't have an XTAL. 

    Best regards,

    Edvin

  • Amazing, thank you so much! I'll give this a go as soon as I get back to my DK and let you know the results!

    I honestly would never have been able to work this out - it's interesting that setting LFCLK to SYNTH causes this behaviour!

    Quick question, what's the best way to conditionally include DEVELOP_IN_NRF52832? I will alternate between the DK and my PCBA, so would prefer not to have to constantly switch.

    edit: apologies for making your life more difficult by modifying the LFCLK value inline instead of defining it at the top!

  • Hey Edvin!

    I set the LFCLK source to RC but unfortunately I was reminded as to why it was set to SYNTH to start with - without setting it to SYNTH the BLE module would not work.

    I'm looking at getting a hardware review (and adding a external HF clock to the PCBA).

    Nathan

  • Hello Nathan,

    Right. An external LFXTAL is one way (I assume you meant externla LF clock, and not HF clock, since you probably have an external HF clock already. That one is mandatory, but the LF XTAL is optional).

    Yes, the SYNTH is an easy way to make sure that the LFCLK is working properly, but this is not intended for production, as it is way too power-hungry. 

    Let me try to get this straight, so that we get the RC oscillator up and running:

    There are several sets of definitions in sdk_config.h regarding the clock configuration. If you search this file for "LF_SRC", you will find:

    NRFX_CLOCK_CONFIG_LF_SRC
    CLOCK_CONFIG_LF_SRC
    NRF_SDH_CLOCK_LF_SRC

    Try to make these align. You can check the file apply_old_config.h to see how NRFX_CLOCK_CONFIG_LF_SRC and CLOCK_CONFIG_LF_SRC work together, but I usually just set them equal to one another, so I don't need to remember which one overwrites the other. 

    The NRF_SDH_CLOCK_LF_SRC is not part of apply_old_config.h, but it is used by the softdevice. When this is set to 0 (NRF_CLOCK_LF_SRC_RC, you need to set NRF_SDH_CLOCK_LF_RC_CTIV, NRF_SDH_CLOCK_LF_RC_TEMP_CTIV and NRF_SDH_CLOCK_LF_ACCURACY accordingly. 

    This is not very intuitive, but if you look in nrf_sdm.h (which will be included in you application project), you can look at the declaration of the struct nrf_clock_lf_cfg_t. In the parameter description it says:

    uint8_t rc_temp_ctiv;   /**<  Only for ::NRF_CLOCK_LF_SRC_RC: How often (in number of calibration
                                    intervals) the RC oscillator shall be calibrated if the temperature
                                    hasn't changed.
                                         0: Always calibrate even if the temperature hasn't changed.
                                         1: Only calibrate if the temperature has changed (legacy - nRF51 only).
                                         2-33: Check the temperature and only calibrate if it has changed,
                                               however calibration will take place every rc_temp_ctiv
                                               intervals in any case.
    
                                    @note Must be 0 if source is not ::NRF_CLOCK_LF_SRC_RC.
    
                                    @note For nRF52, the application must ensure calibration at least once
                                          every 8 seconds to ensure +/-500 ppm clock stability. The
                                          recommended configuration for ::NRF_CLOCK_LF_SRC_RC on nRF52 is
                                          rc_ctiv=16 and rc_temp_ctiv=2. This will ensure calibration at
                                          least once every 8 seconds and for temperature changes of 0.5
                                          degrees Celsius every 4 seconds. See the Product Specification
                                          for the nRF52 device being used for more information.*/

    So if you have NRF_CLOCK_LF_SRC_RC (0), then you must also set:

    NRF_SDH_CLOCK_LF_RC_CTIV 16
    NRF_SDH_CLOCK_LF_RC_TEMP_CTIV 2
    NRF_SDH_CLOCK_LF_ACCURACY 1

    If not, the initialization of the softdevice will fail, as you can see from nrf_sdh.c, line 76:

    #if (   (NRF_SDH_CLOCK_LF_SRC      == NRF_CLOCK_LF_SRC_RC)          \
         && (NRF_SDH_CLOCK_LF_ACCURACY != NRF_CLOCK_LF_ACCURACY_500_PPM))
        #warning Please select NRF_CLOCK_LF_ACCURACY_500_PPM when using NRF_CLOCK_LF_SRC_RC
    #endif
    

    Try that, and let me know whether it works.

    Best regards,

    Edvin

  • Depending on your application you may or may not benefit from adding an LFXTAL. The disadvantage of an external LFCLK is that you have one more component that will draw current, but the advantage is that if you are (often) in a connection, you will have a more accurate LFCLK, which means that the radio will be able to more accurately determine when it will receive a radio packet, which means that it can narrow down the timeslot where the radio will be active (in RX mode). 

    This is however only when you are in a connection. If your application is a beacon that is only advertising, the increased accuracy of the LFCLK doesn't come with any benefits. 

    BR,

    Edvin

Related