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.

  • 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

Reply
  • 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

Children
  • That's interesting, it sounds like (as I'm just using BLE advertising) that a external LFCLK should not be needed then.

    I had set these from reading through the following document which was quite useful (see section for "Internal RC low-frequency oscillator (LFRC)"). The problem I have is that when I set (as you mentioned in your post), the configuration:

    #define CLOCK_CONFIG_LF_SRC 0
    #define NRFX_CLOCK_CONFIG_LF_SRC 0
    #define NRF_SDH_CLOCK_LF_SRC 0
    #define NRF_SDH_CLOCK_LF_RC_CTIV 16
    #define NRF_SDH_CLOCK_LF_RC_TEMP_CTIV 2
    #define NRF_SDH_CLOCK_LF_ACCURACY 1

    the BLE device is no longer detectable. What I mean by this is that I can no longer detect the signal using my laptop or mobile phone (both are using nRF Connect bluetooth app).

    It sounds to me like you're saying this should work. My assumption was that it was a hardware issue which caused the RC oscillator to not be stable enough... I will just double check this with one of the example applications and updating the above configuration. 

  • prsboy said:

    LFCLK should not be needed then.

    In that case, I don't see any benefits from using an LFXTAL, so at least if you haven't designed it in already, I wouldn't bother.

    With the configuration that you have there, when you are not able to detect the BLE device, does the log say anything relevant?

    Is the BLE stack enabled as it should? Does the application reach the advertising_start() function? Do you see any logs at all if you enable logging and use your custom board? Did you remember to remove the DEVELOP_IN_NRF52832 preprocessor definition?

    Best regards,

    Edvin

  • I have completely removed all reference of DEVELOP_IN_NRF52832.

    When i try and deploy it on my custom PCBA I can see logs as usual:

    <info> app_timer: RTC: initialized.
    <info> app: Starting temperature measurement.
    <info> app: Reading temperature data.
    <info> app: Calculated temperature: 22.64 C
    <info> app: number: 2264
    <info> app: Starting humidity measurement.
    <info> app: Reading humidity data.
    <info> app: Calculated humidity: 64.30%
    <info> app: number: 6430
    <info> app: BLE EVENT
    <info> app: start ble
    <info> app: BLE EVENT
    <info> app: Advertising event: Idle
    <info> app: asleep
    <info> app: Awake and start advertising
    <info> app: Starting temperature measurement.
    <info> app: Reading temperature data.
    <info> app: Calculated temperature: 22.75 C
    <info> app: number: 2275
    <info> app: Starting humidity measurement.
    <info> app: Reading humidity data.
    <info> app: Calculated humidity: 63.59%
    <info> app: number: 6359
    <info> app: BLE EVENT
    <info> app: start ble
    <info> app: BLE EVENT
    <info> app: Advertising event: Idle
    <info> app: asleep

    but I'm unable to pick anything up when scanning for the device which is very strange, the device behaves exactly how it does when I set the LFCLK to XTAL, apart from I can't detect the BLE signal. Is there any way I'm able to check the state of the LFCLK? I tried setting

    #define NRFX_CLOCK_CONFIG_LOG_ENABLED 1
    #define NRFX_CLOCK_CONFIG_LOG_LEVEL 4

    but no additional logs are output to the console (as seen from the console output above).

  • prsboy said:
    I have completely removed all reference of DEVELOP_IN_NRF52832.

    What exactly does that mean? I just meant that you should remove it from your preprocessor definitions if you are actually running on an nRF52810 chip.

    1: Are you actually running it on an nRF52810?

    2: Did you remove it from your preprocessor definitions in SES?

    prsboy said:
    #define NRFX_CLOCK_CONFIG_LOG_ENABLED 1 #define NRFX_CLOCK_CONFIG_LOG_LEVEL 4

    From apply_old_config.h:

    #if defined(CLOCK_CONFIG_LOG_ENABLED)
    #undef NRFX_CLOCK_CONFIG_LOG_ENABLED
    #define NRFX_CLOCK_CONFIG_LOG_ENABLED  CLOCK_CONFIG_LOG_ENABLED
    #endif
    #if defined(CLOCK_CONFIG_LOG_LEVEL)
    #undef NRFX_CLOCK_CONFIG_LOG_LEVEL
    #define NRFX_CLOCK_CONFIG_LOG_LEVEL  CLOCK_CONFIG_LOG_LEVEL
    #endif

    So try to set CLOCK_CONFIG_LOG_ENABLED = 1 as well, and CLOCK_CONFIG_LOG_LEVEL = 4.

    You can verify the values of NRFX_CLOCK_CONFIG_LOG_ENABLED by printing it to your log from your main.c file:

    NRF_LOG_INFO("clock log enabled: %d, log_level: %d", NRFX_CLOCK_CONFIG_LOG_ENABLED, NRFX_CLOCK_CONFIG_LOG_LEVEL);

    Another check you can do in your application is to add this before you initiate your timers:

    void check_clock_source(void)
    {
      NRF_LOG_INFO("checking LFCLK ...");
      NRF_CLOCK->TASKS_LFCLKSTART = 1;
      while (NRF_CLOCK->EVENTS_LFCLKSTARTED == false)
      {
        // wait...
      }
      NRF_LOG_INFO("LFCLK started!");
    
    }
    
    int main(void)
    {
        ...
        LOG_INIT()
        ...
        check_clock_source();
        ...
        ble_stack_init()

    And see if that function returns, or if it is stuck. I believe it should return, because you shouldn't be able to initiate the softdevice without an LFCLK.

    If you don't figure it out, can you please upload the updated application that can reproduce the issue you are seeing on a DK?

    Best regards,

    Edvin

  • What exactly does that mean? I just meant that you should remove it from your preprocessor definitions if you are actually running on an nRF52810 chip.

    1: Are you actually running it on an nRF52810?

    2: Did you remove it from your preprocessor definitions in SES?

    1. Yes, I was deploying it on my PCBA (I am using the NRF52DK to flash the PCBA).

    2. I removed it from everywhere I found it:

    https://gist.github.com/scripter-co/47fad092cd1815eed748b9d47f33a4a2

    But I also did exactly what you said in your message and just removed it through SES from the preprocessor and the same result (no BLE signal detected).

    ---------

    I also added the debug/log messages:

    <info> app: checking LFCLK ...
    <info> app: LFCLK started!
    <info> app_timer: RTC: initialized.
    <info> CLOCK: Function: nrfx_clock_init, error code: NRF_SUCCESS.
    <info> clock: Function: nrf_drv_clock_init, error code: NRF_SUCCESS.
    <info> app: Starting temperature measurement.
    <info> app: Reading temperature data.
    <info> app: Calculated temperature: 22.66 C
    <info> app: number: 2266
    <info> app: Starting humidity measurement.
    <info> app: Reading humidity data.
    <info> app: number: 5953
    <info> app: BLE EVENT
    <info> app: start ble
    

    so it seems that everything is working as it should.

    ---- 5 hours later ----

    Oh! I've spent the the whole day looking into this and I've noticed that the custom board is actually advertising - despite the interval set to advertise every 5 seconds (as an example), when I scan for the device using both laptop/phone, it takes a very long time to pick up the signal (5 mins after started searching):

    the DK is picked up straight away. I just happened to notice this after I changed the configuration of NRF Connect to not timeout after 2 minutes and then notice it show up 5 minutes later.

    So I guess the question now is, what could cause the BLE advertising to be so intermittent when deployed to the custom PCBA when using RC oscillator? The signal is not regular - the advertising packets are received very sporadically and unreliably. 

    edit:

    I've played around with the advertising interval and timeout and increasing/decreasing the `ble_adv_fast_interval` makes no difference to the ability to pick anything up. Even when I set ble_adv_fast_timeout = 0 it still behaves the same.

Related