Precision of an advertising interval and clock calibration

Hello,

 I got a custom board based on nRF52840. It uses extended BLE advertising to deliver a sensor data to the central hub with a frequency of 10 Hz. I've set the interval_max to 100ms/0.625 = 160, and interval_min to 159, as by the BT spec they cannot be equal (setting to 160 doesn't change the picture though).

In the test, conducted against Linux machine - I see that the distance between the received packets is ~103-105 ms (average over ~1000 packets). I can tweak it to become 100 by reducing the interval value by 5-7 0.625th - but the correction has to be found individually for each board instance. 

The boards are factory-made and both oscillator circuits made according to a reference design. I use NRFConnect v. 2.5.1
My configuration:

CONFIG_DK_LIBRARY=y
CONFIG_NEWLIB_LIBC=y
CONFIG_NEWLIB_LIBC_FLOAT_PRINTF=y

CONFIG_LOG=n
CONFIG_ASSERT=n

CONFIG_USE_SEGGER_RTT=n
CONFIG_LOG_BACKEND_RTT=n
CONFIG_LOG_MODE_IMMEDIATE=n

CONFIG_BT=y
CONFIG_BT_PERIPHERAL=y
CONFIG_BT_CTLR_PHY_CODED=y
CONFIG_BT_CTLR_ADV_EXT=y
CONFIG_BT_CTLR_ADV_DATA_LEN_MAX=255
CONFIG_BT_EXT_ADV=y
CONFIG_BT_USER_PHY_UPDATE=y
CONFIG_BT_DEVICE_NAME_DYNAMIC=y
CONFIG_BT_RX_STACK_SIZE=4096

CONFIG_BOOTLOADER_MCUBOOT=y

CONFIG_NRF_APPROTECT_LOCK=y

CONFIG_HWINFO=y
CONFIG_BOARD_ENABLE_DCDC=n


CONFIG_HEAP_MEM_POOL_SIZE=8192

CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=4096
CONFIG_MAIN_STACK_SIZE=4096


Code:

#include <stddef.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
#include <zephyr/kernel.h>
#include <zephyr/device.h>
#include <zephyr/drivers/hwinfo.h>

#include <zephyr/bluetooth/bluetooth.h>
#include <zephyr/bluetooth/conn.h>
#include <zephyr/bluetooth/gatt.h>
#include <zephyr/bluetooth/uuid.h>
#include <dk_buttons_and_leds.h>
#include <app_version.h>

#include <zephyr/logging/log.h>
//LOG_MODULE_REGISTER(MN, LOG_LEVEL_INF);

enum
{
    UNIT_0_625_MS = 625,        /**< Number of microseconds in 0.625 milliseconds. */
    UNIT_1_25_MS  = 1250,       /**< Number of microseconds in 1.25 milliseconds. */
    UNIT_10_MS    = 10000       /**< Number of microseconds in 10 milliseconds. */
};

/**@brief Macro for converting milliseconds to ticks.
 *
 * @param[in] TIME          Number of milliseconds to convert.
 * @param[in] RESOLUTION    Unit to be converted to in [us/ticks].
 */
#define MSEC_TO_UNITS(TIME, RESOLUTION) (((TIME) * 1000) / (RESOLUTION))

#define ADV_INTERVAL_NORMAL 100
#define ADV_INTERVAL_HIGHSAMPLING 100

#define BLE_ADV_INTERVAL		MSEC_TO_UNITS(ADV_INTERVAL_NORMAL, UNIT_0_625_MS)

#define INITIAL_DELAYSTART 3000
#define COMPANY_ID				0x1234
#define ADV_DATA_PROTOCOL_VER	0x02

static uint8_t _deviceName[21];

static struct bt_le_ext_adv *_advertiser;
static uint8_t _advMfgData[24];
static uint32_t _advInterval = BLE_ADV_INTERVAL;
static bool _advInit = false;
static uint16_t _delayToStartAdv = 0;

static struct bt_data _advData[] = {
	BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)),
	BT_DATA(BT_DATA_NAME_COMPLETE, _deviceName, sizeof(_deviceName)-1),
	BT_DATA(BT_DATA_MANUFACTURER_DATA, _advMfgData, sizeof(_advMfgData))
};

static struct bt_le_adv_param _advParam;

int main(void)
{
	uint64_t hwinfo = 0;
	int err;

	err = dk_leds_init();
	if (err) {
//		LOG_ERR("LEDs init failed (err %d)", err);
	}

	err = bt_enable(NULL);
	if (err) {
//		LOG_ERR("Bluetooth init failed (err %d)", err);
		return 0;
	}

//	LOG_INF("Bluetooth initialized");

	/* Get hardware ID */
	hwinfo_get_device_id((uint8_t*)&hwinfo, sizeof(hwinfo));
//	LOG_INF("HWId: %016llx", hwinfo);	
	
	uint32_t hwIdMsb = 0, hwIdLsb = 0;

	_delayToStartAdv = INITIAL_DELAYSTART + (hwinfo % INITIAL_DELAYSTART);

	_advParam.id = BT_ID_DEFAULT;
	_advParam.sid = 0;
	_advParam.secondary_max_skip = 0;
	_advParam.options = (BT_LE_ADV_OPT_CONNECTABLE | BT_LE_ADV_OPT_EXT_ADV | BT_LE_ADV_OPT_CODED);
	_advParam.interval_max = _advInterval-7; 
	_advParam.interval_min = _advInterval-7; 
	_advParam.peer = NULL;
	err = bt_le_ext_adv_create(&_advParam, NULL, &_advertiser);
	if (err) {
//		LOG_ERR("Failed to create advertiser set (err %d)", err);
		return err;
	}

	_advInit = true;

	/* Update device name */
	memset(_deviceName, 0, sizeof(_deviceName));
	hwIdMsb = (hwinfo >> 32);
	hwIdLsb = (uint32_t)hwinfo;
	sprintf((char*)_deviceName, "AAA_%08x%08x", hwIdMsb, hwIdLsb);

	err = bt_set_name((const char*)_deviceName);
	if (err) {
//		LOG_ERR("Failed to set device name (err %d)", err);
	}	

	/* Update manufacturing data */
	_advMfgData[0] = (uint8_t)COMPANY_ID;
	_advMfgData[1] = (uint8_t)(COMPANY_ID >> 8);
	_advMfgData[2] = ADV_DATA_PROTOCOL_VER;

	_advData[2].data = _advMfgData;

	err = bt_le_ext_adv_set_data(_advertiser, _advData, ARRAY_SIZE(_advData), NULL, 0);

	if (err) {
//		LOG_ERR("Failed to set advertising data (err %d)", err);
		return err;
	}

    err = bt_le_ext_adv_start(_advertiser, NULL);
	if (err) {
//		LOG_ERR("Failed to start advertising set (err %d)", err);
		return;
	}


}

Please advise

  • Hi Paskin,

    It's great that you have referred to the BT specification to find your answer. There seems to be some misunderstanding, and I would be happy to help clear them up.

    I've set the interval_max to 100ms/0.625 = 160, and interval_min to 159, as by the BT spec they cannot be equal (setting to 160 doesn't change the picture though).

    The Bluetooth spec does not require Connection Interval Min to be smaller than Connection Interval Max at all. All specifications are along the line of "min shall not be greater than max."

    Refer to Vol 4, Part E, 7.8.5, LE Set Advertising Parameters.

    In the test, conducted against Linux machine - I see that the distance between the received packets is ~103-105 ms (average over ~1000 packets). I can tweak it to become 100 by reducing the interval value by 5-7 0.625th - but the correction has to be found individually for each board instance. 

    There is a random 0-10ms delay added to every advertising event. That is why you see the average interval between advertising packets being about 105ms.

    Refer to Vol 6, Part B, 4.4.2.2.1 Advertising interval.

    Hieu

  • Hello Hieu,

    Thanks for your response.

    By "BT Spec" I referred to the comment in NRF Connect header v.2.5.1/zephyr/include/zephyr/bluetooth/bluetooth.h, which in lines 739-740, as well as 748-749 reads:

    Maximum Advertising Interval should not be the same value (as stated in Bluetooth Core Spec 5.2, section 7.8.5)

    Referred section of the Core Spec - contains the following sentence:

    The Advertising_Interval_Min and Advertising_Interval_Max should not be the same value to enable the Controller to determine the best advertising interval given other activities.

    Considering your comment about random delays - Is there a way to send advertisements with the constant interval? I am trying to implement a sensor, which - when triggered - transmits readings as frequently as possible and with high precision of a time line. Unfortunately, it cannot know who is listening and there could be multiple listeners as well (i.e. directed advertising is not possible). 

    Please advise.

  • Hi Paskin,

    In requirement language, "should" means not mandatory, but recommended.
    In the Bluetooth Core Specification, this is described in Vol 1, Part E, Section 1, Language Convention.

    So, you can use the same value. The reason for not doing so is explained in the text you quoted.

    However, it doesn't seem like permitting a range is of interest to you any way, considering that you want exact timing.

    For exact timing, you can look into the Periodic Advertising feature. It does require the scanner to also support Periodic Advertising.
    You can read more about it here: What You Need to Know About Periodic Advertising Sync Transfer | BluetoothRegistered Technology Website.

Related