1. 程式人生 > >nrf51822教程 第一課 BLE Advertising 藍芽廣播

nrf51822教程 第一課 BLE Advertising 藍芽廣播

What you see in the picture is:

    • The device name as defined in our main.c file. Try to search for #define DEVICE_NAME in your code and change the string to something of your own choice (just don’t make it too long). Now compile and download your application again and see how your device name has changed.
    • The Bluetooth Device Address. Similar to a Media Access Control address (MAC address), but not necessarily universally unique.
    • The Received Signal Strength Indicator (RSSI). This is a value of how good your signal strength is. Move your kit around or lay your hand over the PCB antenna on your kit and see how the RSSI changes.

The Advertising packet

Now expand the "HelloWorld" device tree by clicking on the little cross to the left. You should see something similar to the next image. Your first advertising packet

What you see here is:

RSSI

The Received Signal Strength Indicator as mentioned above.

Address

The Bluetooth Device Address as mentioned above.

Address Type

Address type as defined in the Bluetooth Core specification. See this post on devzone for more on address types. The default address type in our example is “Random static address”. Try to go intogap_params_init() in main.c and add these four lines at the end of the function:
ble_gap_addr_t gap_address;
gap_address.addr_type = BLE_GAP_ADDR_TYPE_RANDOM_PRIVATE_RESOLVABLE;
err_code = sd_ble_gap_address_set(BLE_GAP_ADDR_CYCLE_MODE_AUTO,&gap_address);
APP_ERROR_CHECK(err_code);// Check for errors

This will set the address type to “Private Resolvable address”.The Address type will still show up as “Random”, but try to reset your board a few times. You should see that after each reset your device shows up with a different address in the device list. This functionality might be handy in certain situations demanding higher security. Remove the lines again to change the address type back to “Random static address”.

Advertising Type

To keep it simple there are two advertising types; connectable and non-connectable. Non-connectable might be useful if you just need one-way communication and only need to broadcast a couple of bytes. One use case for this might for example be location awareness. If you want to send more data to a specific device, on the other hand, you need a connectable device. Examples here might be a heart rate sensor connected to an app on your phone or a Bluetooth headset.

Bonded

This shows whether the device is bonded to another device or not. The purpose of bonding is described in the Bluetooth Core Specification 4.2 as:
  • "The purpose of bonding is to create a relation between two Bluetooth devices based on a common link key (a bond). The link key is created and exchanged (pairing) during the bonding procedure and is expected to be stored by both Bluetooth devices, to be used for future authentication[...]"

Advertising Data

Now to the fun part! Go to the advertising_init() function. The advertising data fields contained in the advdata variable are the fields where you can configure your first advertising packet. The advertising packet in this example already contains your full device name. It also contains flags defining some of the advertising options. The flag field is mandatory, but we will discuss it here.

Search for the definition of the ble_advdata_t advertising data structure in your project files (right click on ble_advdata_t and click "Go To Definition Of 'ble_advdata_t'. This works on all functions and variables). When you find it you can see that it is a list of options and data structures with some very brief descriptions. There are quite a few options, but it is important to know that an advertising packet can consist of no more than 31 bytes, so you might need to select your data with care. Hereis a useful discussion on advertising bytes.

First let us play with the name and pretend that we need a really long one. Find the "DEVICE_NAME" define again and change the string to “ThisIsAReallyLongName”. Compile and download the code and make sure your new name shows in the device list. This name consumes a lot of our precious 31 byte long advertising packet. So let us say we want to keep our long name, but only advertise the first few letters of it. The advertising_init() function should now look something like this:

staticvoid advertising_init(void){uint32_t      err_code;ble_advdata_t advdata;// Struct containing advertising parameters// Build advertising data struct to pass into @ref ble_advertising_init.
    memset(&advdata,0,sizeof(advdata));

    advdata.name_type               = BLE_ADVDATA_FULL_NAME;
    advdata.flags                   = BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE;ble_adv_modes_config_t options ={0};
    options.ble_adv_fast_enabled  = BLE_ADV_FAST_ENABLED;
    options.ble_adv_fast_interval = APP_ADV_INTERVAL;
    options.ble_adv_fast_timeout  = APP_ADV_TIMEOUT_IN_SECONDS;

    err_code = ble_advertising_init(&advdata, NULL,&options, on_adv_evt, NULL);
    APP_ERROR_CHECK(err_code);}

Change the advertising name type to:

advdata.name_type = BLE_ADVDATA_SHORT_NAME;// Use a shortened name

Then right below this add the line:

advdata.short_name_len =6;// Advertise only first 6 letters of name

Compile and download again. In the “Discovered devices” list your device should now show up with the name “ThisIs”, the first 6 letters of the full name. However, if you connect to your device by clicking “Select device”, “Connect” and then “Discover services” you will see that the full name is still there. In our case, this frees up 15 bytes in the advertising packet.

Manufacturer Specific Data

One of the most interesting fields in the advertising data structure might be thep_manuf_specific_data field. This is a field where you can place whatever data you want. So copy theadvertising_init() function below:
staticvoid advertising_init(void){uint32_t      err_code;ble_advdata_t advdata;// Struct containing advertising parametersble_advdata_manuf_data_t        manuf_data;// Variable to hold manufacturer specific datauint8_t data[]="SomeData!";// Our data to adverise
    manuf_data.company_identifier       =0x0059;// Nordics company ID
    manuf_data.data.p_data              = data;     
    manuf_data.data.size                =sizeof(data);// Build advertising data struct to pass into @ref ble_advertising_init.
    memset(&advdata,0,sizeof(advdata));

    advdata.name_type               = BLE_ADVDATA_SHORT_NAME;
    advdata.short_name_len          =6;
    advdata.flags                   = BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE;
    advdata.p_manuf_specific_data   =&manuf_data;ble_adv_modes_config_t options ={0};
    options.ble_adv_fast_enabled  = BLE_ADV_FAST_ENABLED;
    options.ble_adv_fast_interval = APP_ADV_INTERVAL;
    options.ble_adv_fast_timeout  = APP_ADV_TIMEOUT_IN_SECONDS;

    err_code = ble_advertising_init(&advdata, NULL,&options, on_adv_evt, NULL);
    APP_ERROR_CHECK(err_code);}

Now compile, download and check your MCP. You can see that there is a new field in the “Advertising Data” list called “ManufacturerSpecificData”. It contains a cryptic string of hexadecimal numbers. The first two numbers are the company ID (0x0059) with the least significant byte first. The company ID is a unique 16-bit value assigned to Bluetooth SIG members. It is not allowed to broadcast a custom company ID value without being a member. If you are curious you can see a list of the Bluetooth SIG member IDs here. As described at the bottom of the page the value 0xFFFF "may be used in the internal and interoperability tests before a Company ID has been assigned. This value shall not be used in shipping end products". When you are ready for shipping you will need to apply for a Bluetooth SIG membership to get your own unique ID. "Adopter level" membership is free, but you will need to be associated with a valid company. The next 10 bytes in the manufacturer specific data field are the actual data. If you compare with an ASCII table you will recognize the string “SomeData!”, most significant byte first this time. The last two zeroes at the end represent a termination character in the string.

Challenge 1: Try to add and populate the "p_tx_power_level" field in "advdata" with a value of your own choice and see what happens in MCP. This field is meant to advertise the transmit power of your device. This value might, e.g., enable other units to roughly estimate the distance to your device. (Keep in mind that you will have to use one of the valid tx power levels shown in Chapter 8.5.3 in the nRF51 Product Specification V3.1. It is also important to know that this doesn't change the actual transmit power, just the information that is advertised).

int8_t tx_power_level =4;
advdata.p_tx_power_level =&tx_power_level;

Challenge 2: Set the "include_appearance" field in "advdata" to "true" and see what happens in MCP. Then search for sd_ble_gap_appearance_set(0) in your main file and change the ‘0’ to“BLE_APPEARANCE_HID_MOUSE” and see what happens. The appearance feature might be useful to developers of e.g. mobile phone apps. If you have a BLE enabled phone (or a laptop) your kit should now show up with the name "ThisIs" and a computer mouse icon if you search for BLE devices.

Tips: Your advertisement packet is already getting dangerously full so you might want to shorten your manufacturer data size or device name.

This is what I can see on my phone:

Scan response data

But what if you really, really want to advertise more than 31 bytes? Then there is a solution for you and that is the Scan response data. This is an optional "secondary" advertising payload which allows scanning devices that detect an advertising device to request a second advertising packet. This allows you to send two advertising frames with a total payload of 62 bytes.

Challenge: In your code, immediately above the line err_code = ble_advdata_set(&advdata, NULL), do the following:

  1. Declare a new "ble_advdata_manuf_data_t" type variable called "manuf_data_response".
  2. Populate "manuf_data_response" with data as we did with the first advertising packet.
  3. Declare a new "ble_advdata_t" type variable called "advdata_response".
  4. Populate "advdata_response" with name type "BLE_ADVDATA_NO_NAME".
  5. Populate "advdata_response" with your new manufacturer specific data.
  6. Change the line

    err_code = ble_advertising_init(&advdata, NULL, &options, on_adv_evt, NULL);

    to

    err_code = ble_advertising_init(&advdata, advdata_response, &options, on_adv_evt, NULL);
  7. Compile, download and see what happens.

This should make your device look something like this in MCP: Advertising with Scan response

In case you need help your advertising_init() function should now look similar to this:

staticvoid advertising_init(void){uint32_t      err_code;ble_advdata_t advdata;// Struct containing advertising parametersble_advdata_manuf_data_t        manuf_data;// Variable to hold manufacturer specific datauint8_t data[]="SomeData!";// Our data to adverise
    manuf_data.company_identifier       =0x0059;// Nordics company ID
    manuf_data.data.p_data              = data;     
    manuf_data.data.size                =sizeof(data);// Build advertising data struct to pass into @ref ble_advertising_init.
    memset(&advdata,0,sizeof(advdata));

    advdata.name_type               = BLE_ADVDATA_SHORT_NAME;
    advdata.short_name_len          =3;
    advdata.flags                   = BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE;
    advdata.p_manuf_specific_data   =&manuf_data;int8_t tx_power                 =-4;
    advdata.p_tx_power_level        =&tx_power;
    advdata.include_appearance      =true;// Prepare the scan response Manufacturer specific data packetble_advdata_manuf_data_t                manuf_data_response;uint8_t                                 data_response[]="Many_bytes_of_data";// Remember there is a 0 terminator at the end of string
    manuf_data_response.company_identifier       =0x0059;               
    manuf_data_response.data.p_data              = data_response;        
    manuf_data_response.data.size                =sizeof(data_response);ble_advdata_t   advdata_response;// Declare and populate a scan response packet// Always initialize all fields in structs to zero or you might get unexpected behaviour
    memset(&advdata_response,0,sizeof(advdata_response));// Populate the scan response packet
    advdata_response.name_type               = BLE_ADVDATA_NO_NAME; 
    advdata_response.p_manuf_specific_data   =&manuf_data_response;ble_adv_modes_config_t options ={0};
    options.ble_adv_fast_enabled  = BLE_ADV_FAST_ENABLED;
    options.ble_adv_fast_interval = APP_ADV_INTERVAL;
    options.ble_adv_fast_timeout  = APP_ADV_TIMEOUT_IN_SECONDS;

    err_code = ble_advertising_init(&advdata,&advdata_response,&options, on_adv_evt, NULL);
    APP_ERROR_CHECK(err_code);}

Summary

And there you go. Your first nRF5x DK kit advertising its name, various options, and a small payload. Once again I recommend that you take a look at this blog post: Bluetooth Smart and the Nordic's Softdevices - Part 1 GAP Advertising. The next step will be to add services and characteristics: BLE Services, a beginner's tutorial