blob: 558b5526ab2fccadc50e89671580fa6420b32f93 [file] [log] [blame]
Advertising
===========
.. contents::
:local:
:depth: 2
Overview
^^^^^^^^
A peripheral announces its presence to the world by broadcasting
advertisements. An advertisement typically contains additional
information about the peripheral sending it, such as the device name and
an abbreviated list of supported services. The presence of this
information helps a listening central to determine whether it is
interested in connecting to the peripheral. Advertisements are quite
limited in the amount of information they can contain, so only the most
important information should be included.
When a listening device receives an advertisement, it can choose to
connect to the peripheral, or query the sender for more information.
This second action is known as an *active scan*. A peripheral responds
to an active scan with some extra information that it couldn't fit in
its advertisement. This additional information is known as *scan
response data*. *bleprph* does not configure any scan response data, so
this feature is not discussed in the remainder of this tutorial.
*bleprph* constantly broadcasts advertisements until a central connects
to it. When a connection is terminated, *bleprph* resumes advertising.
Let's take a look at *bleprph*'s advertisement code (*main.c*):
.. code:: c
/**
* Enables advertising with the following parameters:
* o General discoverable mode.
* o Undirected connectable mode.
*/
static void
bleprph_advertise(void)
{
struct ble_hs_adv_fields fields;
int rc;
/* Set the advertisement data included in our advertisements. */
memset(&fields, 0, sizeof fields);
fields.name = (uint8_t *)bleprph_device_name;
fields.name_len = strlen(bleprph_device_name);
fields.name_is_complete = 1;
rc = ble_gap_adv_set_fields(&fields);
if (rc != 0) {
BLEPRPH_LOG(ERROR, "error setting advertisement data; rc=%d\n", rc);
return;
}
/* Begin advertising. */
rc = ble_gap_adv_start(BLE_GAP_DISC_MODE_GEN, BLE_GAP_CONN_MODE_UND,
NULL, 0, NULL, bleprph_on_connect, NULL);
if (rc != 0) {
BLEPRPH_LOG(ERROR, "error enabling advertisement; rc=%d\n", rc);
return;
}
}
Now let's examine this code in detail.
Setting advertisement data
^^^^^^^^^^^^^^^^^^^^^^^^^^
A NimBLE peripheral specifies what information to include in its
advertisements with the following function:
.. code:: c
int
ble_gap_adv_set_fields(struct ble_hs_adv_fields *adv_fields)
The *adv\_fields* argument specifies the fields and their contents to
include in subsequent advertisements. The Bluetooth `Core Specification
Supplement <https://www.bluetooth.org/DocMan/handlers/DownloadDoc.ashx?doc_id=302735>`__
defines a set of standard fields that can be included in an
advertisement; the member variables of the *struct ble\_hs\_adv\_fields*
type correspond to these standard fields. Information that doesn't fit
neatly into a standard field should be put in the *manufacturing
specific data* field.
As you can see in the above code listing, the
``struct ble_hs_adv_fields`` instance is allocated on the stack. It is
OK to use the stack for this struct and the data it references, as the
``ble_gap_adv_set_fields()`` function makes a copy of all the
advertisement data before it returns. *bleprph* doesn't take full
advantange of this; it stores its device name in a static array.
The code sets three members of the *struct ble\_hs\_adv\_fields*
instance:
- name
- name\_len
- name\_is\_complete
The first two fields are used to communicate the device's name and are
quite straight-forward. The third field requires some explanation.
Bluetooth specifies two name-related advertisement fields: *Shortened
Local Name* and *Complete Local Name*. Setting the ``name_is_complete``
variable to 1 or 0 tells NimBLE which of these two fields to include in
advertisements. Some other advertisement fields also correspond to
multiple variables in the field struct, so it is a good idea to review
the *ble\_hs\_adv\_fields* reference to make sure you get the details
right in your app.
Begin advertising
^^^^^^^^^^^^^^^^^
An app starts advertising with the following function:
.. code:: c
int
ble_gap_adv_start(uint8_t discoverable_mode, uint8_t connectable_mode,
uint8_t *peer_addr, uint8_t peer_addr_type,
struct hci_adv_params *adv_params,
ble_gap_conn_fn *cb, void *cb_arg)
This function allows a lot of flexibility, and it might seem daunting at
first glance. *bleprph* specifies a simple set of arguments that is
appropriate for most peripherals. When getting started on a typical
peripheral, we recommend you use the same arguments as *bleprph*, with
the exception of the last two (*cb* and *cb\_arg*). These last two
arguments will be specific to your app, so let's talk about them.
*cb* is a callback function. It gets executed when a central connects to
your peripheral after receiving an advertisement. The *cb\_arg* argument
gets passed to the *cb* callback. If your callback doesn't need the
*cb\_arg* parameter, you can do what *bleprph* does and pass *NULL*.
Once a connection is established, the *cb* callback becomes permanently
associated with the connection. All subsequent events related to the
connection are communicated to your app via calls to this callback
function. Connection callbacks are an important part of building a BLE
app, and we examine *bleprph*'s connection callback in detail in the
next section of this tutorial.
**One final note:** Your peripheral automatically stops advertising when
a central connects to it. You can immediately resume advertising if you
want to allow another central to connect, but you will need to do so
explicitly by calling ``ble_gap_adv_start()`` again. Also, be aware
NimBLE's default configuration only allows a single connection at a
time. NimBLE supports multiple concurrent connections, but you must
configure it to do so first.