How to correctly disable BLE advertising?

Hi,

I've come across several posts on this topic, but they all seem to be older than two years. Unfortunately, most of them don't provide any useful information.

Currently, I've implemented a small code example, where I start BLE advertisement upon a button press using interrupts. I then start a timer, and upon its expiration, I stop the BLE advertisement.

I found out that you can not disable BLE, as it is not implemented on the low level, link. If run my code, I get this error: 

Upon examining the hci_core.c code, it becomes apparent that the k_sem_take() function yields a result of -11, corresponding to the error code EAGAIN.:

I know how to interpret it correctly, but to me it seems like the resource is not available, possibly due to timing restrictions. However, this shouldn't be the case, considering the timeout is set to 10 seconds (HCI_CMD_TIMEOUT = 10 sec). Any clarification or assistance on this matter would be greatly appreciated.

This is my code:

#include <zephyr/kernel.h>
#include <zephyr/logging/log.h>
#include <zephyr/bluetooth/bluetooth.h>
#include <zephyr/bluetooth/gap.h>

#include <dk_buttons_and_leds.h>

bool is_running = false;
LOG_MODULE_REGISTER(test_ble, LOG_LEVEL_INF);

#define DEVICE_NAME CONFIG_BT_DEVICE_NAME
#define DEVICE_NAME_LEN (sizeof(DEVICE_NAME) - 1)

#define RUN_STATUS_LED DK_LED1
#define RUN_LED_BLINK_INTERVAL 1000


void my_timer_handler(struct k_timer *dummy);
void my_work_handler(struct k_work *work);
K_TIMER_DEFINE(my_timer, my_timer_handler, NULL);
K_WORK_DEFINE(my_work, my_work_handler);


static const struct bt_data ad[] = {
	BT_DATA_BYTES(BT_DATA_FLAGS, BT_LE_AD_NO_BREDR),
	BT_DATA(BT_DATA_NAME_COMPLETE, DEVICE_NAME, DEVICE_NAME_LEN),

};

// Declare the URL data to include in the scan response 
static unsigned char url_data[] = { 0x17, '/', '/', 'a', 'c', 'a', 'd', 'e', 'm',
				    'y',  '.', 'n', 'o', 'r', 'd', 'i', 'c', 's',
				    'e',  'm', 'i', '.', 'c', 'o', 'm' };



static const struct bt_data sd[] = {
	BT_DATA(BT_DATA_URI, url_data, sizeof(url_data)),
};

void start_ble(){
	int err;

	/* this is currently not supported:
	https://devzone.nordicsemi.com/f/nordic-q-a/102668/trying-to-start-and-stop-ble-periodically
	err = bt_enable(NULL);
	if (err) {
		LOG_ERR("Bluetooth init failed (err %d)\n", err);
		return;
	}
	*/

	err = bt_le_adv_start(BT_LE_ADV_NCONN, ad, ARRAY_SIZE(ad), sd, ARRAY_SIZE(sd));
	if (err) {
		LOG_ERR("Advertising failed to start (err %d)\n", err);
		return;
	}

}

void stop_ble(){
	int err;
	
	err = bt_le_adv_stop();
	if (err) {
		LOG_ERR("Advertising failed to stop (err %d)\n", err);
		return;
	}

	/* this is currently not supported:
	https://devzone.nordicsemi.com/f/nordic-q-a/102668/trying-to-start-and-stop-ble-periodically
	err = bt_disable();
	if (err) {
		LOG_ERR("Bluetooth stop failed (err %d)\n", err);
		return;
	}
	*/
}

void my_timer_handler(struct k_timer *dummy)
{
    LOG_INF("Timer expired\n");
	stop_ble();
	is_running = false;
}

void my_work_handler(struct k_work *work)
{
	LOG_INF("Work started. \n");
 	start_ble();
	k_timer_start(&my_timer, K_SECONDS(8), K_NO_WAIT);
}


static void button_changed(uint32_t button_state, uint32_t has_changed)
{
	if (has_changed & DK_BTN1_MSK)
	{
		LOG_INF("Btn 1 pressed! \n");
		if(!is_running){
			k_work_submit(&my_work);
			is_running = true;

		}
	}
}

int main(void)
{
	int blink_status = 0;
	int err;

	err = dk_leds_init();
	if (err) {
		LOG_ERR("LEDs init failed (err %d)\n", err);
		return -1;
	}

	err = dk_buttons_init(button_changed);
	if (err)
	{
		printk("Buttons init failed (err %d)\n", err);
		return -1;
	}

	err = bt_enable(NULL);
	if (err) {
		LOG_ERR("Bluetooth init failed (err %d)\n", err);
		return -1;
	}
	LOG_INF("started successfully.\n");

	while (true) {
		dk_set_led(RUN_STATUS_LED, (++blink_status) % 2);
		k_sleep(K_MSEC(RUN_LED_BLINK_INTERVAL));
	}
}

One thing that also bothers me is that the interrupt routine is triggered twice, even though I have only pressed the button once. Why is this the case?

Thanks for your help.

  • Hi,

    The timer callback, where stop_ble() is called, runs in the RTC interrupt context. I recommend you offload this task to the workque or another thread. Also, it should be possible to disable the BLE stack in more recent SDK version, but this should only be necessary if you need to reclaim resources reserved by the stack, e.g., the RADIO IRQ. 

    Best regards,

    Vidar

  • Thanks, it worked! What a silly mistake on my part, but good to know for the future.

    I would like to switch off the radio completely to minimize energy consumption. What kind of resources do I need to reclaim and what exactly do I need to do with these resources? I thought calling a stop_radio() function should do the trick.

    Is there a code example of how this should be done correctly?

  • Glad to hear! Thanks for the update. Normally, I would have expected this issue to have been caught by an assertion, but maybe the ASSERTs weren't enabled in your build. Either way, it was not obvious what the error was from the crash log alone.

    Disabling the Bluetooth host will not reduce the current consumption. The RADIO is only kept active during protocol events anyway (during scanning, advertising, and connection events). Otherwise, it is powered down. But if you still want to disable it, you can try to do what Edvin suggested in this post:  RE: Boot up without SoftDevice possible? (nRF Connect)  

Related