This discussion has been locked.
You can no longer post new replies to this discussion. If you have a question you can start a new discussion

no mfg data during scan response when bt_data_parse

Hello,
the device name is being parsed ok, but the mfg data in scan response seems to be there but not sure how to print it out.
Using ncs bt sample direction_finding_connectionless_rx modified for nRF52840DK since it was using bt_le_scan_recv_info and bt_data_parse.
The nrf terminal is showing the name for Thingy-1, but not the output for mfg of data_len 6 during case BT_DATA_MANUFACTURER_DATA.

/*
 * Copyright (c) 2021 Nordic Semiconductor ASA
 *
 * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
 */

#include <stddef.h>
#include <errno.h>
#include <zephyr.h>

#include <bluetooth/bluetooth.h>


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

/* BT Core 5.3 specification allows controller to wait 6 periodic advertising events for
 * synchronization establishment, hence timeout must be longer than that.
 */
#define SYNC_CREATE_TIMEOUT_INTERVAL_NUM 7
/* Maximum length of advertising data represented in hexadecimal format */
#define ADV_DATA_HEX_STR_LEN_MAX (BT_GAP_ADV_MAX_EXT_ADV_DATA_LEN * 2 + 1)

static volatile bool per_adv_found;
static bool scan_enabled;

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

	switch (data->type) {
	case BT_DATA_NAME_SHORTENED:
	case BT_DATA_NAME_COMPLETE:
		len = MIN(data->data_len, PEER_NAME_LEN_MAX - 1);
		memcpy(name, data->data, len);
		printk("name: %s\n", name);	
		printk("[AD name]: %u data_len %u\n", data->type, data->data_len);
		name[len] = '\0';
		return false;
	case BT_DATA_MANUFACTURER_DATA:
		printk("mfg: %s\n", name);	
		printk("[AD mfg]: %u data_len %u\n", data->type, data->data_len);
		return false;
	default:
		return true;
	}
}


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[PEER_NAME_LEN_MAX];

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

	bt_data_parse(buf, data_cb, name);

	bt_addr_le_to_str(info->addr, le_addr, sizeof(le_addr));

	// printk("[DEVICE]: %s, AD evt type %u, Tx Pwr: %i, RSSI %i %s C:%u S:%u "
	//        "D:%u SR:%u SID: %u\n",
	//        le_addr, info->adv_type, info->tx_power, info->rssi, 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->sid);

	printk("scan_recv: %s\n", name);	
}

static struct bt_le_scan_cb scan_callbacks = {
	.recv = scan_recv,
};


static int scan_init(void)
{
	printk("Scan callbacks register...");
	bt_le_scan_cb_register(&scan_callbacks);
	printk("success.\n");

	return 0;
}

static int scan_enable(void)
{
	struct bt_le_scan_param param = {
		.type = BT_LE_SCAN_TYPE_ACTIVE,
		.options = BT_LE_SCAN_OPT_FILTER_DUPLICATE,
		.interval = BT_GAP_SCAN_FAST_INTERVAL,
		.window = BT_GAP_SCAN_FAST_WINDOW,
		.timeout = 0U,
	};

	int err;

	if (!scan_enabled) {
		printk("Start scanning...");
		err = bt_le_scan_start(&param, NULL);
		if (err) {
			printk("failed (err %d)\n", err);
			return err;
		}
		printk("success\n");
		scan_enabled = true;
	}

	return 0;
}


void main(void)
{
	int err;

	printk("Starting Connectionless Locator Demo\n");

	printk("Bluetooth initialization...");
	err = bt_enable(NULL);
	if (err) {
		printk("failed (err %d)\n", err);
	}
	printk("success\n");

	scan_init();

	scan_enabled = false;

	while (true) {
		per_adv_found = false;
		scan_enable();
	}
}

How best to get mfg data in scan response?
(based on case, https://devzone.nordicsemi.com/f/nordic-q-a/84753/scan-and-parse-manufacturer-specific-data-zephyr/353172#353172)

thank-you,

Parents
  • Hello,

    I believe you already have the manufacturer data in your scan response, but you are trying to print it as if it was a character string (which a name will be, but the manufacturer data is probably not that). 

    Try to do something like:

    case BT_DATA_MANUFACTURER_DATA:
        printk("Manufacturer data: ");
        for (uint16_t i=0; i<data->len; i++)
        {
            printk("%02x:", data->data[i]);
        }
        printk("\n");
        break;

    As mentioned, the manufacturer data is not necessarily a character string, so you need to know up front what the manufacturer data represents, as it is completely proprietary. Perhaps it is a measured temperature, the advertised TX Power, or something completely else.

    Best regards,

    Edvin

Reply
  • Hello,

    I believe you already have the manufacturer data in your scan response, but you are trying to print it as if it was a character string (which a name will be, but the manufacturer data is probably not that). 

    Try to do something like:

    case BT_DATA_MANUFACTURER_DATA:
        printk("Manufacturer data: ");
        for (uint16_t i=0; i<data->len; i++)
        {
            printk("%02x:", data->data[i]);
        }
        printk("\n");
        break;

    As mentioned, the manufacturer data is not necessarily a character string, so you need to know up front what the manufacturer data represents, as it is completely proprietary. Perhaps it is a measured temperature, the advertised TX Power, or something completely else.

    Best regards,

    Edvin

Children
  • That's how,...ok.

    If I could add here BT_SCAN_NAME_FILTER from this case,
    https://devzone.nordicsemi.com/f/nordic-q-a/79991/ble-scan-filter-by-device-name/331924

    A name filter is set in scan_init, but BT_DATA_NAME_COMPLETE in data_cb is picking up other devices than what is filtered.

    I tested BT_SCAN_NAME_FILTER with central_uart and peripheral_uart filtering for Nordic_UART_Service and Thingy-1, and central_uart only connected when name Filters matched.

    Checking to confirm that data_cb is in fact separate from what is set in scan_init and will process all devices regardless of what is filtered, or that data_cb should be part of the filter and for some reason is not filtering?

    CONFIG_BT=y
    CONFIG_BT_DEVICE_NAME="DF Connectionless Locator App"
    
    CONFIG_BT_EXT_ADV=y
    CONFIG_BT_PER_ADV_SYNC=y
    CONFIG_BT_OBSERVER=y
    
    # Enable Direction Finding Feature including AoA and AoD
    CONFIG_BT_DF=y
    CONFIG_BT_DF_CONNECTIONLESS_CTE_RX=y
    
    #Bluetooth
    CONFIG_BT_CENTRAL=y
    CONFIG_BT_SMP=y
    CONFIG_BT_GATT_CLIENT=y
    CONFIG_BT_GATT_DM=y
    CONFIG_HEAP_MEM_POOL_SIZE=2048
    
    #Custom central scanning
    CONFIG_BT_SCAN=y
    CONFIG_BT_SCAN_FILTER_ENABLE=y
    CONFIG_BT_SCAN_NAME_CNT=1
    CONFIG_BT_SCAN_MANUFACTURER_DATA_CNT=1
    CONFIG_BT_PRIVACY=y

    /*
     * Copyright (c) 2021 Nordic Semiconductor ASA
     *
     * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
     */
    
    #include <stddef.h>
    #include <errno.h>
    #include <zephyr.h>
    
    #include <bluetooth/bluetooth.h>
    
    #include <string.h>
    #include <stdlib.h>
    #include <zephyr/types.h>
    
    #include <bluetooth/conn.h>
    #include <bluetooth/gatt.h>
    #include <bluetooth/hci.h>
    #include <bluetooth/uuid.h>
    #include <bluetooth/services/throughput.h>
    #include <bluetooth/scan.h>
    #include <bluetooth/gatt_dm.h>
    
    
    #define DEVICE_NAME CONFIG_BT_DEVICE_NAME
    #define DEVICE_NAME_LEN (sizeof(DEVICE_NAME) - 1)
    #define PEER_NAME_LEN_MAX 30
    
    /* BT Core 5.3 specification allows controller to wait 6 periodic advertising events for
     * synchronization establishment, hence timeout must be longer than that.
     */
    #define SYNC_CREATE_TIMEOUT_INTERVAL_NUM 7
    /* Maximum length of advertising data represented in hexadecimal format */
    #define ADV_DATA_HEX_STR_LEN_MAX (BT_GAP_ADV_MAX_EXT_ADV_DATA_LEN * 2 + 1)
    
    static volatile bool per_adv_found;
    static bool scan_enabled;
    
    static bool data_cb(struct bt_data *data, void *user_data)
    {
    	char *name = user_data;
    	uint8_t len;
    
    	switch (data->type) {
    	case BT_DATA_NAME_SHORTENED:
    	case BT_DATA_NAME_COMPLETE:
    		len = MIN(data->data_len, PEER_NAME_LEN_MAX - 1);
    		memcpy(name, data->data, len);
    		printk("name: %s\n", name);	
    		printk("[AD name]: %u data_len %u\n", data->type, data->data_len);
    		name[len] = '\0';
    		return false;
    	case BT_DATA_MANUFACTURER_DATA:
    		printk("Manufacturer data: ");
    		printk("[AD mfg]: %u data_len %u\n", data->type, data->data_len);
    		for (uint16_t i=0; i<6; i++)
    		{
    			printk("%02x:", data->data[i]);
    		}
    		printk("\n");
    		return false;
    	default:
    		return true;
    	}
    }
    
    
    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[PEER_NAME_LEN_MAX];
    
    	(void)memset(name, 0, sizeof(name));
    
    	bt_data_parse(buf, data_cb, name);
    
    	bt_addr_le_to_str(info->addr, le_addr, sizeof(le_addr));
    
    	// printk("[DEVICE]: %s, AD evt type %u, Tx Pwr: %i, RSSI %i %s C:%u S:%u "
    	//        "D:%u SR:%u SID: %u\n",
    	//        le_addr, info->adv_type, info->tx_power, info->rssi, 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->sid);
    
    	printk("scan_recv: %s\n", name);	
    }
    
    static struct bt_le_scan_cb scan_callbacks = {
    	.recv = scan_recv,
    };
    
    
    static int scan_init(void)
    {
    	int err;
    
    	char device_thingy[8] = "Thingy-1";	
    	char *thingy = device_thingy;
    	// thingy[8] = '\0';
    	printk("thingy: %s\n", thingy);	
    
    	printk("Scan callbacks register...");
    	bt_le_scan_cb_register(&scan_callbacks);
    	printk("success.\n");
    
    	err = bt_scan_filter_add(BT_SCAN_FILTER_TYPE_NAME, thingy);
    	if (err) {
    		printk("Scanning filters cannot be set\n");
    		return err;
    	}
    
    	err = bt_scan_filter_enable(BT_SCAN_NAME_FILTER, false);
    	if (err) {
    		printk("Filters cannot be turned on\n");
    		return err;
    	}
    
    
    	return 0;
    }
    
    static int scan_enable(void)
    {
    	struct bt_le_scan_param param = {
    		.type = BT_LE_SCAN_TYPE_ACTIVE,
    		.options = BT_LE_SCAN_OPT_FILTER_DUPLICATE,
    		.interval = BT_GAP_SCAN_FAST_INTERVAL,
    		.window = BT_GAP_SCAN_FAST_WINDOW,
    		.timeout = 0U,
    	};
    
    	int err;
    
    	if (!scan_enabled) {
    		printk("Start scanning...");
    		err = bt_le_scan_start(&param, NULL);
    		if (err) {
    			printk("failed (err %d)\n", err);
    			return err;
    		}
    		printk("success\n");
    		scan_enabled = true;
    	}
    
    	return 0;
    }
    
    
    void main(void)
    {
    	int err;
    
    	printk("Starting Connectionless Locator Demo\n");
    
    	printk("Bluetooth initialization...");
    	err = bt_enable(NULL);
    	if (err) {
    		printk("failed (err %d)\n", err);
    	}
    	printk("success\n");
    
    	scan_init();
    
    	scan_enabled = false;
    
    	while (true) {
    		per_adv_found = false;
    		scan_enable();
    	}
    }

    thank-you,

  • Hello,

    There are still some very important differences between the central_uart sample and your setup with regards to filtering. 

    First, a minor detail:

    in scan_init() in the central_uart sample, you can see that it says .connfect_if_match = 1, so this means that when the filter is matched, then it will connect to that peripheral. 

    Then, there is a difference in how the callbacks are set up.

    You use bt_le_scan_cb_register() to register the .recv. This callback will trigger on every scan event. What you probably want to do is to create a filter_match callback. Look at how this is done in the central_uart sample:

    BT_SCAN_CB_INIT(scan_cb, scan_filter_match, NULL,
    		scan_connecting_error, scan_connecting);
    
    
    static int scan_init(void)
    {
    	int err;
        struct bt_scan_init_param scan_init = {
    		.connect_if_match = 1,
    	};
    
    	bt_scan_init(&scan_init);
    	bt_scan_cb_register(&scan_cb);

    So the macto BT_SCAN_CB_INIT() creates a callback struct called scan_cb, with several callback functions. scan_filter_match, which will trigger whenever there is a filter match, scan_connecting_error which will trigger if they can't connect, and scan_connecting which will trigger upon connection. In case you want a callback when you don't have a filter match, but receive an advertising report, you can insert that instead of the "NULL" in BT_SCAN_CB_INIT().

    The filter match callback is on this form:

    	void (*filter_match)(struct bt_scan_device_info *device_info,
    			     struct bt_scan_filter_match *filter_match,
    			     bool connectable);

    So replace "(*filter_match)" with the name you want to use for the callback function, and implement it in your main.c. You can see how this is done i the central_uart sample.

    Best regards,

    Edvin

  • Originally an attempt was made to use scan_filter_match to filter beacon like devices and read the scan response.
    But scan_filter_match did not seem to have any access to scan response and mfg data.
    Also it seems that scan_filter_match is a one off,
    unlike something like .recv which can read new advertised data "on every scan event".
    A filter is setup in BT_DATA_MANUFACTURER_DATA to search for unique bytes in the scan response,
    which seems to be working ok.
    Wanted to double check if scan_filter_match could be a better way.

    thank-you,

  • I guess it doesn't really matter how you do it. The filter match events are just generated whenever an advertisement matched the filter. If you use the built in filters, or create a filter manually wouldn't really make a difference for preformance, so if you have something that is working, I see no need for changing that.

    BR,
    Edvin

Related