How to read advertisement data per device using a central device

Hi all

I am using the bluetooth central sample from zephyr. I can read the addresses of the devices but I would like to read each specific device's advertisement data. So, when the central device finds the ID AA:BB:CC:DD:EE:FF, i would like to read the advertisement data from this device. Does anyone have any advice for how I can go about doing this? Thanks

Parents
  • Personally, I like the NCS\zephyr\samples\bluetooth\central.

    Out of the box, this sample just connects to anything it sees, but f you modify the device_found() callback, it holds the advertising data information.

    The struct net_buf_simple *ad parameter holds the advertising, and you can print it by using something like:

    device_found(const bt_addr_le_t *addr, int8_t rssi, uint8_t type,
    			 struct net_buf_simple *ad)
    {
        LOG_INF("received advertisement, length %d", ad->len);
        for (int i=0; i<ad->len; i++){
            LOG_PRINTK("ad->data[i]:");
        }
        // Not sure if the below line is needed. Uncomment if it only receives one advertisement.
        // start_scan();
    }

    This also requires CONFIG_LOG_PRINTK=y in your prj.conf.

    The reason I use LOG_PRINTK() is that this doesn't add a timestamp and newline for every instance of LOG_PRINTK(). You can also use printk() for everything if you prefer, but I agree with   that you should switch to the log module sooner rather than later.

    Best regards,

    Edvin

  • Hi Edvin,

    Thanks for the reply. This makes sense to me. However, when I try this my advertisement data is not quite matching up.

    My code in device_found:

    My output

    Result from my c209 tag via the NRF Connect App

    I assume i should be getting the values from the "data" field

    Seems like the data fields are even different lengths too. Why would the NRF Connect App be seeing something different?

  • Hey Regina,

    I am providing a rough implementation of what i think will suit your need.

    Try this:

    // Add bt_data_parse to parse the advertisement packet inside your if(strncmp(addr_str, c209_id, 15)== 0){

    bt_data_parse(ad, parse_message, (void *addr);

    }

    and then you can use something like this...

     

    static bool parse_message(struct bt_data *data, void *user_data){
    	bt_addr_le_t *adv_addr = user_data;
    	// All data type definitions available in zephyr/bluetooth/gap.h
    	switch(data->type){
    		case BT_DATA_LE_BT_DEVICE_ADDRESS:
    			LOG_INF("Device address: %.*s\n", data->data_len, (char *)data->data);
    		break;
    
    		case BT_DATA_NAME_COMPLETE:
    		case BT_DATA_NAME_SHORTENED:
    			LOG_INF("Device name: %s", data->data);
    		break;
    
    		default:
    			// print raw data
    			LOG_INF("AD type 0x%02X, length %u\n", data->type, data->data_len);
    			for (int i = 0; i < data->data_len; i++) {
    				LOG_PRINTK("%02X ", data->data[i]);
    			}
    			LOG_PRINTK("\n");
    		break;
    
    	}
    	if (conn_exist_check(adv_addr)) {
    		/* Device is already connected, stop parsing the adv data */
    		return false;
    	}
    	return true; // return true if you want to continue parsing or false based on a condition to exit.
    }

  • This is a good way to represent the advertisement! Thanks for sharing,  !

    ReginaA101 said:
    My output

    I believe part of the issue here is that you are printing "%x", not specifying the amount of digits to print for each byte. Therefore, I prefer to separate each byte by ":", but I see that that was wrong in my previous reply. I didn't specify the print format. It should be:

    LOG_PRINTK("%02x:",ad->data[i]);

    or printk("%02x:",ad->data[i]);

    In this case, it would print "02:" if the byte was 2, where as if you only write printk("%x", ad->data[i]); it will only print "2". 

    This is probably a good time to introduce the DevAcademy course "Bluetooth Low Energy Fundamentals", and particularly Lesson 2, which talks about the format of the advertising packet. There you can see that each segment in the advertising packet consists of one byte indicating the length, followed by one byte indicating the type of advertisement segments data type, followed by the actual data for that segment. Search for the figure called "Example of an advertising data structure", and you will see the structure of each segment in the advertising packet.

    The different advertising data types can be found here:

    https://docs.nordicsemi.com/bundle/zephyr-apis-latest/page/group_bt_gap_defines.html

    I can't link directly to the table, but search that page for "EIR/AD data type definitions"

    So in your log, it looks like it says that the first segment is 0x21 bytes long = 33 bytes long, but this is not possible. A normal advertising packet can be maximum 31 bytes long. I believe this is where the '0' before the '2', '1' and '6' is not printed. I believe it should say:

    (0x)020106, which would mean, length 2 (including the type), type 1 (flags), value 6, which means "BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR". 

    Your advertising packet claims to be 21 bytes, but you only printed 17 and a half byte, so there are probably a lot of bytes starting with 0 that are not printed correctly. Guessing which ones is a bit tedious, so I suggest you try to add the %02x: to separate them properly. 

    But again,  's suggestion looks good. It will probably print the advertising data in a more human readable way. And if there are any of the default AD types that are printed in the log, look them up from the above links, and add a switch case for them as well.

    Best regards,

    Edvin

  • Hi Edvin,

    For some reason, the parse_message function never runs. I discovered this because I tried to print the data from the data.c file, and this are the packets I am receiving: 

    But when I put the checks in the parse_message callback, it doesnt do anything. 

    This is the setup:

    My guess is that the callback isnt triggering. Any ideas on why?

    Thanks,

    Regina

Reply
  • Hi Edvin,

    For some reason, the parse_message function never runs. I discovered this because I tried to print the data from the data.c file, and this are the packets I am receiving: 

    But when I put the checks in the parse_message callback, it doesnt do anything. 

    This is the setup:

    My guess is that the callback isnt triggering. Any ideas on why?

    Thanks,

    Regina

Children
Related