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 Reply Children
  • Hi,

    I do not see anyting problematic with the advertising related code. However, I did not understand the explanation about how the device does not seem to be sleeping as you can connect to it via SWD. Can you explain what you mean by the device not sleeping? And how you have tested and verified? Have you checked the current consumptoin or do you test in some other way?

    (If testing current consumption, note that an active debug session will cause a high current consumption due to clocks and reculators being kept on, so you sould check without debugging).

  • I do tests like this:

    - Make some changes in code.

    - Flash new code to the device via J-Link

    - Unplug the device from J-Link

    - Connect the device to PPK2 (set to 3V)

    - Turn on PPK2 power supply

    - Measure power consumption with 100kHz

    When advertise function is called, sleep does not work and device uses ~3.5mA. If advertise function is not called(but BLE::init is called before while(1)) then sleep works and power consumption is around 170uA(ignore that high number, pressure sensor and I2C bus is not implemented yet).

    I did not try to disable Softdevice before going to sleep. I'll try that since it seems like it's the only option for sleep

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

Related