Sleep does not work with BLE advertise

Hi,

nRF52832, SDK 17.1.0 with Softdevice S132.

Softdevice sleep does not work when advertising. Application flow is to advertise one packet, then go to sleep for X seconds. Sleep part waits for a flag from "BLE_GAP_EVT_ADV_SET_TERMINATED" event.

If "sd_app_evt_wait" is replaced with "__WFI" when sleep works but BLE does not advertise anymore(even BLE_GAP_EVT_ADV_SET_TERMINATED is received).

Any idea how to put device to sleep?

This code works

	//BLE::advertise(&data, sizeof(data));

	while (1)
	{
		/*if (System::isWoken() == Return_t::OK)
		{
			BLE::advertise(&data, sizeof(data));
		}*/

		//if (BLE::isAdvertiseDone() == Return_t::OK)
		if (1)
		{
			System::startWakeupTimer();
			System::sleep();
		}

		// Feed the dog
		System::feedWatchdog();
	}

This code does not work(consumption around ~3.5mA)

	BLE::advertise(&data, sizeof(data));

	while (1)
	{
		if (System::isWoken() == Return_t::OK)
		{
			BLE::advertise(&data, sizeof(data));
		}

		if (BLE::isAdvertiseDone() == Return_t::OK)
		//if (1)
		{
			System::startWakeupTimer();
			System::sleep();
		}

		// Feed the dog
		System::feedWatchdog();
	}

sleep function:

	void sleep(void)
	{
		__set_FPSCR(__get_FPSCR()  & ~(0x0000009F));      
		(void) __get_FPSCR();
		sd_nvic_ClearPendingIRQ(FPU_IRQn);		

		sd_power_dcdc_mode_set(NRF_POWER_DCDC_DISABLE);

		_PRINT("Sleep\n");
		sd_app_evt_wait();

		sd_power_dcdc_mode_set(NRF_POWER_DCDC_ENABLE);
	}

Only RTC2 IRQ is enabled. FPU is disabled(soft FPU is used).

System init

	Return_t init(void)
	{
		// Test crystals only in debug build
		#ifdef DEBUG
		_PRINT("Wait for HFXO\n");
		NRF_CLOCK->EVENTS_HFCLKSTARTED = 0;
		NRF_CLOCK->TASKS_HFCLKSTART = 1;
		while (NRF_CLOCK->EVENTS_HFCLKSTARTED == 0);
		NRF_CLOCK->EVENTS_HFCLKSTARTED = 0;	
		_PRINT("HFXO started\n");

		_PRINT("Wait for LFXO\n");
		NRF_CLOCK->EVENTS_LFCLKSTARTED = 0;
		NRF_CLOCK->TASKS_LFCLKSTART = 1;
		while (NRF_CLOCK->EVENTS_LFCLKSTARTED == 0);
		NRF_CLOCK->EVENTS_LFCLKSTARTED = 0;	
		_PRINT("LFXO started\n");		
		#endif // DEBUG

		// Configure power
		NRF_POWER->POFCON = 0;
		NRF_POWER->DCDCEN = 1;

		// Init watchdog
		NRF_WDT->CRV = (AppConfig::wdtTimeout * 32768) - 1;
		#ifdef DEBUG
		NRF_WDT->CONFIG = 1;
		#else
		NRF_WDT->CONFIG = 9;
		#endif // DEBUG
		NRF_WDT->TASKS_START = 1;

		// Enable RTC interrupt
		ret_code_t ret = sd_nvic_SetPriority(RTC2_IRQn, 2);
		if (ret != NRF_SUCCESS)
		{
			APP_ERROR_CHECK(ret);
			return Return_t::NOK;
		}

		ret = sd_nvic_EnableIRQ(RTC2_IRQn);
		if (ret != NRF_SUCCESS)
		{
			APP_ERROR_CHECK(ret);
			return Return_t::NOK;
		}

		// Enable compare0 interrupt
		NRF_RTC2->INTENSET = (1 << 16);

		// Set counter resolution of 125ms
		NRF_RTC2->PRESCALER = 4095;

		startWakeupTimer();
		return Return_t::OK;
	}
	
	void startWakeupTimer(void)
	{
		NRF_RTC2->TASKS_CLEAR = 1;
		NRF_RTC2->CC[0] = (AppConfig::measurePeriod * 1000) / 125;
		NRF_RTC2->TASKS_START = 1;
		_PRINT_INFO("Wakeup timer started\n");
	}

EDIT: BLE init functions

	Return_t init(void)
	{
		ret_code_t ret;

		ret = nrf_sdh_enable_request();
		if (ret != NRF_SUCCESS)
		{
			APP_ERROR_CHECK(ret);
			return Return_t::NOK;
		}
	
		// Configure the BLE stack using the default settings.
		uint32_t ramStart = 0;
		ret = nrf_sdh_ble_default_cfg_set(AppConfig::bleTag, &ramStart);
		if (ret != NRF_SUCCESS)
		{
			APP_ERROR_CHECK(ret);
			return Return_t::NOK;
		}
	
		// Enable BLE stack.
		ret = nrf_sdh_ble_enable(&ramStart);
		if (ret != NRF_SUCCESS)
		{
			APP_ERROR_CHECK(ret);
			return Return_t::NOK;
		}
	
		// Register a handler for BLE events.
		NRF_SDH_BLE_OBSERVER(m_ble_observer, 3, onBLEEvent, NULL);

		// Init GAP profile
		if (gapInit() != Return_t::OK)
		{
			return Return_t::NOK;
		}	

		// Get MAC address
		ble_gap_addr_t addr;
		ret = sd_ble_gap_addr_get(&addr);
		if (ret != NRF_SUCCESS)
		{
			APP_ERROR_CHECK(ret);
			return Return_t::NOK;
		}		
		_PRINTF_INFO("MAC: %02X%02X%02X%02X%02X%02X\n", addr.addr[5], addr.addr[4], addr.addr[3], addr.addr[2], addr.addr[1], addr.addr[0]);

		// Request HFXO
		ret = sd_clock_hfclk_request();
		if (ret != NRF_SUCCESS)
		{
			APP_ERROR_CHECK(ret);
			return Return_t::NOK;
		}		

		// Init advertise
		return advInit();
	}

	/**
	 * @brief Advertise data.
	 * 
	 * @return \c Return_t::NOK on fail.
	 * @return \c Return_t::OK on success.
	 */
	Return_t advertise(const void* data, const uint8_t len)
	{
		// Set custom data
		ble_advdata_manuf_data_t mnfData;
		
		mnfData.company_identifier = AppConfig::bleMnfID;
		mnfData.data.p_data = (uint8_t*)data;
		mnfData.data.size = len;
	
		// Set advertise data
		ble_advdata_t advData;
		memset(&advData, 0, sizeof(advData));
		advData.name_type = BLE_ADVDATA_FULL_NAME;
		advData.flags = BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE;
		advData.p_manuf_specific_data = &mnfData;
		advData.p_tx_power_level = &txPower;
	
		// Encode advertise data
		ret_code_t ret = ble_advdata_encode(&advData, gapAdvData.adv_data.p_data, &gapAdvData.adv_data.len);
		if (ret != NRF_SUCCESS)
		{	
			APP_ERROR_CHECK(ret);
			return Return_t::NOK;
		}

		// Advertise data
		advDone = 0;
		ret = sd_ble_gap_adv_start(advHandle, AppConfig::bleTag);
		if (ret != NRF_SUCCESS)
		{
			APP_ERROR_CHECK(ret);
			return Return_t::NOK;
		}		

		return Return_t::OK;
	}
	
/**
 * @brief Init BLE GAP profile.
 * 
 * @return \c Return_t::NOK on fail.
 * @return \c Return_t::OK on success.
 */
static Return_t gapInit(void)
{
	ble_gap_conn_sec_mode_t securtiyMode;
	BLE_GAP_CONN_SEC_MODE_SET_OPEN(&securtiyMode);

	ret_code_t ret = sd_ble_gap_device_name_set(&securtiyMode, (const uint8_t*)AppConfig::deviceName, __CONST_STR_LEN(AppConfig::deviceName));
	if (ret != NRF_SUCCESS)
	{
		APP_ERROR_CHECK(ret);
		return Return_t::NOK;
	}

	return Return_t::OK;
}

/**
 * @brief Init BLE advertise.
 * 
 * @return \c Return_t::NOK on fail.
 * @return \c Return_t::OK on success. 
 */
static Return_t advInit(void)
{
	// Set advertise config
	advConfig.primary_phy = BLE_GAP_PHY_AUTO;
	advConfig.duration = 0;
	advConfig.properties.type = BLE_GAP_ADV_TYPE_NONCONNECTABLE_SCANNABLE_UNDIRECTED;
	advConfig.max_adv_evts	= AppConfig::advCount;
	advConfig.p_peer_addr = nullptr;
	advConfig.filter_policy = BLE_GAP_ADV_FP_ANY;
	advConfig.interval = 32; // Does not matter since max advertise event is set to 1 

	ret_code_t ret = sd_ble_gap_adv_set_configure(&advHandle, &gapAdvData, &advConfig);
	if (ret != NRF_SUCCESS)
	{	
		APP_ERROR_CHECK(ret);
		return Return_t::NOK;
	}

	// Set TX power
	ret = sd_ble_gap_tx_power_set(BLE_GAP_TX_POWER_ROLE_ADV, advHandle, txPower);
	if (ret != NRF_SUCCESS)
	{	
		APP_ERROR_CHECK(ret);
		return Return_t::NOK;
	}		

	return Return_t::OK;
}

Parents
  • Hi,

    When using a SoftDevice you should wait by calling sd_app_evt_wait(), and not WFI (or WFE for that matter) directly. Advertising in itself should not cause any probem, should not contribute to current consumption in between advertising events. Can you show the implementation of your BLE::advertise(), as it is including that that increases the current consumption?

  • Hi,

    BLE functions are added to main post.

    Seems like device does not go to sleep since I can connect to it via SWD(J-Link). I guess some BLE event prevents sleep.
    I tried __WFI just for test otherwise SD sleep function is used.

  • Hi,

    I see. There is no need to disable the SoftDevice in order to sleep, but you must call sd_app_evt_wait() in you rmain loop. However, I see that you only call your sleep() function when BLE::isAdvertiseDone(). This looks like a mistake. You should always call sd_app_evt_wait() from you rmain loop.

  • 	//BLE::advertise(&data, sizeof(data));
    
    	while (1)
    	{
    		/*if (System::isWoken() == Return_t::OK)
    		{
    			BLE::advertise(&data, sizeof(data));
    		}*/
    
    		//if (BLE::isAdvertiseDone() == Return_t::OK)
    		if (1)
    		{
    			System::startWakeupTimer();
    			System::sleep();
    		}
    
    		// Feed the dog
    		System::feedWatchdog();
    	}

    This is in main loop. There is no RTOS.

    "BLE::isAdvertiseDone()" checks advertise flag. Device waits for advertise to end to go to sleep.

    This is BLE event handler

    static void onBLEEvent(ble_evt_t const* event, void* context)
    {
    	_PRINTF_INFO("BLE EVT %u\n", event->header.evt_id);
    	ret_code_t ret = NRF_SUCCESS;
    
    	switch (event->header.evt_id)
    	{
    		case BLE_GAP_EVT_DISCONNECTED:
    		{
    			_PRINT_INFO("BLE disconnected\n");
    			break;
    		}
    
    		case BLE_GAP_EVT_CONNECTED:
    		{
    			_PRINT_INFO("BLE connect\n");
    			ret = sd_ble_gap_disconnect(event->evt.gattc_evt.conn_handle,
    				BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION);
    			APP_ERROR_CHECK(ret);
    			break;
    		}
    
    		case BLE_GAP_EVT_PHY_UPDATE_REQUEST:
    		{
    			_PRINT_INFO("PHY update request\n");
    			static const ble_gap_phys_t phys =
    			{
    				.tx_phys = BLE_GAP_PHY_AUTO,
    				.rx_phys = BLE_GAP_PHY_AUTO,
    			};
    			ret = sd_ble_gap_phy_update(event->evt.gap_evt.conn_handle, &phys);
    			APP_ERROR_CHECK(ret);
    			break;
    		} 
    
    		case BLE_GATTC_EVT_TIMEOUT:
    		{
    			_PRINT_INFO("GATT Client Timeout\n");
    			ret = sd_ble_gap_disconnect(event->evt.gattc_evt.conn_handle,
    												BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION);
    			APP_ERROR_CHECK(ret);
    			break;
    		}
    
    		case BLE_GATTS_EVT_TIMEOUT:
    		{
    			_PRINT_INFO("GATT Server Timeout\n");
    			ret = sd_ble_gap_disconnect(event->evt.gatts_evt.conn_handle,
    												BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION);
    			APP_ERROR_CHECK(ret);
    			break;
    		}
    
    		case BLE_GAP_EVT_ADV_SET_TERMINATED:
    		{
    			advDone = 1;	
    			_PRINT_INFO("Advertise done\n");
    			break;
    		}
    
    		default: break;
    	}
    }

    This is whole project(dev branch) - main.cpp is in Application folder and System and BLE are in Modules folder. https://github.com/silvio3105/sTPMS_FW/tree/dev

    Should I wait for advertise to end or just go to sleep without waiting?

    If I get you right - I should call SD sleep from main loop only if there is nothing else to do?
    What wakes up the device? I don't expect to be waken up by SoftDevice, only RTC2.

    Something like this:

    switch (state)

    {

    case MEASURE: // Measure pressure

    case ADVERTISE: // Advertise data over BLE

    default: // Sleep

    }

  • Code like this works. Sleeps well wiith little peaks.




    Why are there double peaks? Why Softdevice wakes up the device when it does not scan or advertise? Seems odd to me.

  • Hi,

    silvio3105 said:

    Should I wait for advertise to end or just go to sleep without waiting?

    Go to sleep withotu waiting.

    silvio3105 said:
    If I get you right - I should call SD sleep from main loop only if there is nothing else to do?

    No, always call sd_app_evt_wait() from the main loop. This way, the CPU will enter sleep whenever there is nothing to do, and be woken up when there is work to do (the SoftDevice will wake up on RTC0 for anythign it shoudl do), your application wakes up on other interrupts, for instance RTC2 (also RTC1 if you use the app_timer), and any other interrupt sources (could for instance be GPIO pin interrupt).

    silvio3105 said:

    Something like this:

    No. It is essential that you enter sleep also when advertising. There is no point in spending power on busy waiting the CPU when advertising, whcih most of the time is waiting for the next advertising event.

    silvio3105 said:
    Why are there double peaks?

    I would need to see a much zoomed in view to be able to see what this current consumption pattern could represent (though I notice they are periodic).

    silvio3105 said:
    Why Softdevice wakes up the device when it does not scan or advertise? Seems odd to me.

    Do you use a 32.768 kHz crystall or the internal LFRC? If you use the internal LFRC, you probably (and must) enable regular calibration of the LFRC. The SoftDevice will wake up to perform check temperature and perform calibration, normally every 4 seconds (this is configured when initializing the SoftDevice). This happens a bit too often for that to be likely, but you could check the configuration. Other than that, then SoftDevice will normally only wake up when there are Bluetooth events (scanning, advertising or maintaing a connection).

  • Thanks.

    If advertise is ongoing, MCU will not go to sleep until it's done. I guess Softdevice will reject sleep until it's done with advertise. So I don't see the difference between calling sleep after starting advertise or waiting for flag for advertise is done.
    It is not clear(maybe) but device will advertise only one packet:
    - Measure stuff
    - Advertise measured data only once
    - Sleep for X seconds
    - Repeat

    I use external LXFO(configured in SDK config). Maybe SD wakes up to check is someone wants to connect? I don't need connection, just advertise. Something wakes up the MCU and it's not RTC2.

Reply
  • Thanks.

    If advertise is ongoing, MCU will not go to sleep until it's done. I guess Softdevice will reject sleep until it's done with advertise. So I don't see the difference between calling sleep after starting advertise or waiting for flag for advertise is done.
    It is not clear(maybe) but device will advertise only one packet:
    - Measure stuff
    - Advertise measured data only once
    - Sleep for X seconds
    - Repeat

    I use external LXFO(configured in SDK config). Maybe SD wakes up to check is someone wants to connect? I don't need connection, just advertise. Something wakes up the MCU and it's not RTC2.

Children
  • Hi,

    silvio3105 said:
    If advertise is ongoing, MCU will not go to sleep until it's done. I guess Softdevice will reject sleep until it's done with advertise.

    That depends on what you mean. As long as you call sd_app_evt_wait from the main loop, the SoftDevice will enter sleep whenever it has processed it's interrupts.

  • Okey.

    Is there any way to disable LF calibration from Softdevice?

    In SDK config XTAL is selected, ppm is set to 20 and CTIV values are set to 0. I guess 0 will disable it but I'm not sure.

    Softdevice is used only for advertise. I don't know why I have double peaks and why MCU is woken up when measure/advertise period is 5 seconds.

  • Does DEBUG flag or LOG module has effect on wakeups?
    This is power consumption without DEBUG flag and NRF LOG module. Softdevice is disabled before calling __WFI();
    It looks the same as consumption without DEBUG and LOG module and SD is not disabled before calling SD sleep function.

  • silvio3105 said:
    Does DEBUG flag or LOG module has effect on wakeups?

    No. If you don't use deferred logging, logs will be processed whenever there is logging. If you use deferred logging, you process logs in your main loop before calling sd_app_evt_wait() (as you can see in most SDK examples).

    silvio3105 said:
    This is power consumption without DEBUG flag and NRF LOG module. Softdevice is disabled before calling __WFI();
    It looks the same as consumption without DEBUG and LOG module and SD is not disabled before calling SD sleep function.

    sd_app_evt_wait() will only make the CPU sleep, it does not do anythign else to put the system in a low power mode (like stopping peripherals etc). If you use UART for logging(?) for instance, that will increase power consumption as the UART will stay on also in sleeep.

    If you could zoom in the time scale so we see the actual current consumption pattern of just the peak, with some luck, we may be able to recognize what it could be. Also, are you using a DK or a custom board, and is the DCDC enabled or not? And if not using a DK, what is the supply voltage? (this could help in understanding what wee see in a zoomed in current consumption plot). Perhaps you can also export and update the data from the power analyser so that I can zoom in and look at this on my end?

    silvio3105 said:
    Is there any way to disable LF calibration from Softdevice?

    Calibration is only used when the LFRC is enabled. It is not possible (nor needed) to calibrate the LFXO, so this should not be relevant as you write that. you have configured LFXO.

    silvio3105 said:
    Softdevice is used only for advertise. I don't know why I have double peaks and why MCU is woken up when measure/advertise period is 5 seconds.

    As mentionned, we need to look more closely at the peaks to know what they could represent. And the next is to look at your code. As you use LFXO, the SoftDevice will only wake up for BLE events.

  • I use RTT for logging. Only RTC2 is enabled during sleep. ADC is stopped and disabled on EVENTS_END.

    VDD is 3V from PPK2. Only PPK2 is connected, no J-Link.
    RTC2 wakeup period(and advertise period) is 5s.

    When you say BLE events - what do you mean by BLE events? I use only BLE for advertise. Scanner and connect stuff should be disabled, so no BLE events for that?

    I use custom board

    DCDC is disabled before sleep and enabled after wakeup(due to higher consumption with DCDC on during sleep).

    Code is: https://github.com/silvio3105/sTPMS_FW/tree/dev (dev branch, application and modules folder)

    ppk2-20250325T110356.ppk2

    This is done with DEBUG build(DEBUG flag is added during build, NRF_LOG is enabled over RTT)
    Most os these peaks are 2.5s apart. nRF connect app says period is 5 seconds.

Related