Bluetooth Observer Parse Advertising Manufacturing Data?

Hi,

Before, posting I tried this post below, but it did not work for me. It did not print the manufacturing data.

https://devzone.nordicsemi.com/f/nordic-q-a/84753/scan-and-parse-manufacturer-specific-data-zephyr/353544#353544

This is my device Redmi Note 12S from the serial print out. 

[DEVICE]: 45:98:2E:F7:04:19 (random), AD evt type 5, Tx Pwr: 127, RSSI -63 Data                                                                                                              status: 0, AD data len: 25 Name: Redmi Note 12S C:0 S:0 D:0 SR:0 E:1 Pri PHY: LE                                                                                                              1M, Sec PHY: LE 1M, Interval: 0x0000 (0 ms), SID: 5

For Bluetooth Advertiser I am using nRF Connect App with these settings below.

Here is my code below based from Bluetooth Observer example program.

prj.conf:

CONFIG_BT=y
CONFIG_BT_OBSERVER=y
CONFIG_BT_EXT_ADV=y

main.c:

#include <zephyr/sys/printk.h>
#include <zephyr/bluetooth/bluetooth.h>
#include <zephyr/bluetooth/hci.h>

#define NAME_LEN 30

static void device_found(const bt_addr_le_t *addr, int8_t rssi, uint8_t type,
			 struct net_buf_simple *ad)
{
	char addr_str[BT_ADDR_LE_STR_LEN];

	bt_addr_le_to_str(addr, addr_str, sizeof(addr_str));
	printk("Device found: %s (RSSI %d), type %u, AD data len %u\n",
	       addr_str, rssi, type, ad->len);
}

#if defined(CONFIG_BT_EXT_ADV)
static bool data_cb(struct bt_data *data, void *user_data)
{
	char *name = user_data;
	uint8_t len;

	switch (data->type) {
	case BT_DATA_MANUFACTURER_DATA:
		len = MIN(data->data_len, NAME_LEN - 1);
		memcpy(name, data->data, len);
		name[len] = '\0';
	return false;

	case BT_DATA_NAME_SHORTENED:

	case BT_DATA_NAME_COMPLETE:
		len = MIN(data->data_len, NAME_LEN - 1);
		(void)memcpy(name, data->data, len);
		name[len] = '\0';
		return false;

	default:
		return true;
	}
}

static const char *phy2str(uint8_t phy)
{
	switch (phy) {
	case BT_GAP_LE_PHY_NONE: return "No packets";
	case BT_GAP_LE_PHY_1M: return "LE 1M";
	case BT_GAP_LE_PHY_2M: return "LE 2M";
	case BT_GAP_LE_PHY_CODED: return "LE Coded";
	default: return "Unknown";
	}
}

static void scan_recv(const struct bt_le_scan_recv_info *info,
		      struct net_buf_simple *buf)
{
	char le_addr[BT_ADDR_LE_STR_LEN];
	char name[NAME_LEN];
	uint8_t data_status;
	uint16_t data_len;

	(void)memset(name, 0, sizeof(name));

	data_len = buf->len;
	bt_data_parse(buf, data_cb, name);

	data_status = BT_HCI_LE_ADV_EVT_TYPE_DATA_STATUS(info->adv_props);

   
	bt_addr_le_to_str(info->addr, le_addr, sizeof(le_addr));
	printk("[DEVICE]: %s, AD evt type %u, Tx Pwr: %i, RSSI %i "
	       "Data status: %u, AD data len: %u Name: %s "
	       "C:%u S:%u D:%u SR:%u E:%u Pri PHY: %s, Sec PHY: %s, "
	       "Interval: 0x%04x (%u ms), SID: %u\n",
	       le_addr, info->adv_type, info->tx_power, info->rssi,
	       data_status, data_len, name,
	       (info->adv_props & BT_GAP_ADV_PROP_CONNECTABLE) != 0,
	       (info->adv_props & BT_GAP_ADV_PROP_SCANNABLE) != 0,
	       (info->adv_props & BT_GAP_ADV_PROP_DIRECTED) != 0,
	       (info->adv_props & BT_GAP_ADV_PROP_SCAN_RESPONSE) != 0,
	       (info->adv_props & BT_GAP_ADV_PROP_EXT_ADV) != 0,
	       phy2str(info->primary_phy), phy2str(info->secondary_phy),
	       info->interval, info->interval * 5 / 4, info->sid);
		   
}

static struct bt_le_scan_cb scan_callbacks = {
	.recv = scan_recv,
};
#endif /* CONFIG_BT_EXT_ADV */

int observer_start(void)
{
	struct bt_le_scan_param scan_param = {
		.type       = BT_LE_SCAN_TYPE_PASSIVE,
		.options    = BT_LE_SCAN_OPT_FILTER_DUPLICATE,
		.interval   = BT_GAP_SCAN_FAST_INTERVAL,
		.window     = BT_GAP_SCAN_FAST_WINDOW,
	};
	int err;

#if defined(CONFIG_BT_EXT_ADV)
	bt_le_scan_cb_register(&scan_callbacks);
	printk("Registered scan callbacks\n");
#endif /* CONFIG_BT_EXT_ADV */

	err = bt_le_scan_start(&scan_param, device_found);
	if (err) {
		printk("Start scanning failed (err %d)\n", err);
		return err;
	}
	printk("Started scanning...\n");

	return 0;
}

At data_cb the set data->type is BT_DATA_NAME_COMPLETE. There is no option to set the data->type to BT_DATA_MANUFACTURER_DATA. That is why I am unable to receive the manufacturer data.

Regards,

Markel

  • Hello Markel,

    At data_cb the set data->type is BT_DATA_NAME_COMPLETE. There is no option to set the data->type to BT_DATA_MANUFACTURER_DATA. That is why I am unable to receive the manufacturer data.

    The sample you are working on is only looking for the device name, but it should not be a problem to expand it to also look for the BT_DATA_MANUFACTURER_DATA field in the payload. However, you need to return 'true' if the advertisement name is found to continue parsing the payload.

    static bool data_cb(struct bt_data *data, void *user_data)
    {
    	char *name = user_data;
    	uint8_t len;
    
    	switch (data->type) {
    	case BT_DATA_MANUFACTURER_DATA:
    		printk("Found manuf. data\n);
    		len = MIN(data->data_len, NAME_LEN - 1);
    		memcpy(name, data->data, len);
    		name[len] = '\0';
    	return false;
    
    	case BT_DATA_NAME_SHORTENED:
    
    	case BT_DATA_NAME_COMPLETE:
    		len = MIN(data->data_len, NAME_LEN - 1);
    		(void)memcpy(name, data->data, len);
    		name[len] = '\0';
    		return true;
    
    	default:
    		return true;
    	}
    }

    Best regards,

    Vidar

  • Hi Vidar,

    It is not possible with current code. The call of bt_data_parse() within scan_recv() is only 1 times. Is there an example program where I can get the advertisement data which includes the manufacturing data.

    static void scan_recv(const struct bt_le_scan_recv_info *info,
    		      struct net_buf_simple *buf)
    {
    	char le_addr[BT_ADDR_LE_STR_LEN];
    	char name[NAME_LEN];
    	uint8_t data_status;
    	uint16_t data_len;	
    
    	(void)memset(name, 0, sizeof(name));
    
    	data_len = buf->len;
    	bt_data_parse(buf, data_cb, name);
    	
    	data_status = BT_HCI_LE_ADV_EVT_TYPE_DATA_STATUS(info->adv_props);
       
    	bt_addr_le_to_str(info->addr, le_addr, sizeof(le_addr));
    	printk("[DEVICE]: %s, AD evt type %u, Tx Pwr: %i, RSSI %i "
    	       "Data status: %u, AD data len: %u Name: %s "
    	       "C:%u S:%u D:%u SR:%u E:%u Pri PHY: %s, Sec PHY: %s, "
    	       "Interval: 0x%04x (%u ms), SID: %u\n",
    	       le_addr, info->adv_type, info->tx_power, info->rssi,
    	       data_status, data_len, name,
    	       (info->adv_props & BT_GAP_ADV_PROP_CONNECTABLE) != 0,
    	       (info->adv_props & BT_GAP_ADV_PROP_SCANNABLE) != 0,
    	       (info->adv_props & BT_GAP_ADV_PROP_DIRECTED) != 0,
    	       (info->adv_props & BT_GAP_ADV_PROP_SCAN_RESPONSE) != 0,
    	       (info->adv_props & BT_GAP_ADV_PROP_EXT_ADV) != 0,
    	       phy2str(info->primary_phy), phy2str(info->secondary_phy),
    	       info->interval, info->interval * 5 / 4, info->sid);
    		   
    }

    I have a working Bluetooth Broadcaster running on nRF52840 DK with advertisement data below.

    /* Define and initialize a variable of type adv_mfg_data_type */
    static adv_mfg_data_type adv_mfg_data = {COMPANY_ID_CODE, 0x00, 0x00, 0x00, 0x00, 0x00};
    
    static unsigned char url_data[] = { };
    
    LOG_MODULE_REGISTER(Scoreboard, LOG_LEVEL_INF);
    
    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),
    	/* Include the Manufacturer Specific Data in the advertising packet. */
    	BT_DATA(BT_DATA_MANUFACTURER_DATA, (unsigned char *)&adv_mfg_data, sizeof(adv_mfg_data)),
    };

    I need to parse this advertisement data at another nRF52840 DK with Bluetooth Observer code running. I already ordered the nRF52840 DK and will arrive next week.

    Regards,

    Markel

  • Hello Markel,

    It is not possible with current code. The call of bt_data_parse() within scan_recv() is only 1 times.

    I'm not sure I follow. Did you change the callback function to return 'True' on BT_DATA_NAME_COMPLETE as I suggested?

    Regards,

    Vidar

  • Hi Vidar,

    It worked following the answer in this post below. But, I think I would need to implement filtering.

    https://devzone.nordicsemi.com/f/nordic-q-a/86216/no-mfg-data-during-scan-response-when-bt_data_parse

    Regards,

    Markel

Related