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.

  • 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

  • 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).

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

Children
  • 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.

  • Hello,

    prsboy said:

    ok, I was afraid you removed every instance in the SDK, but that is not the case. Good.

    prsboy said:
    I also added the debug/log messages:

    prsboy said:
    so it seems that everything is working as it should

    These log messages are coming at a regular speed, right (Like within one second)? Or is it significantly slower when you are using RC than when you are using the SYNTH?

    prsboy said:
    just removed it through SES from the preprocessor and the same result

    Good to know. Make sure you select the "common" in the upper left corner before removing it (I tried to mark it in my screenshot). However, if you removed all like you did in the git diff, then that should have been removed as well.

    Advertising every 5 seconds is pretty slow. I see that you tried experimenting with different advertising intervals. Have you tried running one of the simpler samples on your custom board? The ble_app_hrs, for example, isn't dependent on any other peripherals (it uses UART for logging, but you can change that to RTT, or remove it). Also remember to change the clock to the RC oscillator, like you did for your application. Do you see any advertisements there?

    Just to explain my thoughts. Usually when there are issues with custom boards, the clock configuration is one of the first things we look into. Seeing as it works if you are using the SYNTH, this suggests that your HFCLK setup is fine, this for two reasons:

    1: You are able to pick up the advertising packets, meaning that the packets generated by the radio are correct. The HFCLK is used to send these packets. 

    2: The synthesized LFLCK is running of the HFLCK, meaning that - at least if the advertising interval seems correct - the HFCLK is also fine.

    So this leaves the LFCLK. This is only used to keep track of the time between the packets when you are using the softdevice. So it can be quite off, as long as you are only advertising. The only issue would be that the advertising interval would be a bit off (more than the random 0-10ms added delay). 

    I noticed a warning when building your application again, that NRFX_RNG_ENABLED was defined more than once. It may be that your current application is slightly different than mine, but can you please try to run the attached application, and let me know what the (RTT) log says, and whether you are able to see the advertisements:

    output_edvin.zip

    It should print the actual values (compile time) of the clock configuration definitions. Do you see something like this?

    <info> app: NRFX_RNG_ENABLED 1
    <info> app: CLOCK_CONFIG_LF_SRC 0
    <info> app: NRF_SDH_CLOCK_LF_SRC 0
    <info> app: NRF_SDH_CLOCK_LF_RC_CTIV 16
    <info> app: NRF_SDH_CLOCK_LF_RC_TEMP_CTIV 2
    <info> app: NRF_SDH_CLOCK_LF_ACCURACY 1
    <info> app: checking LFCLK ...
    <info> app: LFCLK started!
    <info> app_timer: RTC: initialized.
    <info> app: my addr: D7:72:C7:D6:4A:E1

    If you are still not able to see the advertisements, can you please test the attached project? It should start blinking the LEDs every 1 second (2 second period). Does it seem correct? Or is it way to fast/slow?

    2768.blinky.zip

    (pca10040e\blank\ses)

    Does that work on your board?

    Best regards,

    Edvin

  • Advertising every 5 seconds is pretty slow. I see that you tried experimenting with different advertising intervals. Have you tried running one of the simpler samples on your custom board?

    I misspoke when I said this, sorry. I meant timeout, not interval. So 5 seconds advertising (100ms interval) - sleep for 10 seconds - repeat.

    I noticed a warning when building your application again, that NRFX_RNG_ENABLED was defined more than once.

    I believe that to not be an issue, it's when I was setting up the crypto libraries. I believe it's when I define both

    #define RNG_ENABLED 1
    #define NRFX_RNG_ENABLED 1

    They're the same thing?

    That being said, here is the output of the application you requested:

    <info> app: NRFX_RNG_ENABLED 1
    <info> app: CLOCK_CONFIG_LF_SRC 0
    <info> app: NRF_SDH_CLOCK_LF_SRC 0
    <info> app: NRF_SDH_CLOCK_LF_RC_CTIV 16
    <info> app: NRF_SDH_CLOCK_LF_RC_TEMP_CTIV 2
    <info> app: NRF_SDH_CLOCK_LF_ACCURACY 1
    <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: my addr: D0:07:B7:A4:FC:E2
    <info> app: Starting temperature measurement.
    

    If you are still not able to see the advertisements, can you please test the attached project? It should start blinking the LEDs every 1 second (2 second period). Does it seem correct? Or is it way to fast/slow?

    I couldn't quite get that project setup, so I quickly wrote another one to run on my PCBA that does the same (blinks the light using app timers every 1000ms:

    #include "nrf_drv_clock.h"
    #include "app_timer.h"
    #include "nrf_gpio.h"
    #include "boards.h"
    
    #define LED_PIN 6 // LED connected to pin 6
    APP_TIMER_DEF(m_led_toggle_timer);
    
    static void led_toggle(void * p_context)
    {
        UNUSED_PARAMETER(p_context);
        nrf_gpio_pin_toggle(LED_PIN);
    }
    
    static void timers_init(void)
    {
        // Initialize the app timer module.
        ret_code_t err_code = app_timer_init();
        APP_ERROR_CHECK(err_code);
    
        // Create the timer to toggle the LED every 1 second.
        err_code = app_timer_create(&m_led_toggle_timer, APP_TIMER_MODE_REPEATED, led_toggle);
        APP_ERROR_CHECK(err_code);
    }
    
    static void gpio_init(void)
    {
        // Initialize the GPIO pin as output with initial value as low (LED off)
        nrf_gpio_cfg_output(LED_PIN);
        nrf_gpio_pin_clear(LED_PIN);
    }
    
    int main(void)
    {
        // Initialize the LFCLK required for the timers.
        nrf_drv_clock_init();
        nrf_drv_clock_lfclk_request(NULL);
    
        gpio_init();
        timers_init();
    
        // Start the timer
        ret_code_t err_code = app_timer_start(m_led_toggle_timer, APP_TIMER_TICKS(1000), NULL);
        APP_ERROR_CHECK(err_code);
    
        // Enter main loop.
        while (true)
        {
        }
    }
    

    and the output looks good to me:

    Edit:

    I got the ble_app_hrs_pca10040e_s112 example working on both the DK and custom PCBA. First image is on the DK and second is on the custom PCBA (I removed DEVELOP_IN_NRF52832 when on the PCBA as needed).

  • prsboy said:
    and the output looks good to me

    Ok, that looks good. I can't tell from your snippet, but that is using the RC-oscillator, and not the SYNTH LFCLK_SRC, right?

    Just so I didn't misunderstand something. When you initially tested your own application, and the issue was the high current consumption. Were you able to pick up more advertising packets then? When you were using the SYNTH source for the LF clock? Or was the performance the same?

Related