blob: a7453c4804926175a3d0db16ccec4ae5df11cde5 [file] [log] [blame]
Service Registration
====================
.. contents::
:local:
:depth: 2
Attribute Set
^^^^^^^^^^^^^
The NimBLE host uses a table-based design for GATT server configuration.
The set of supported attributes are expressed as a series of tables that
resides in your C code. When possible, we recommend using a single
monolithic table, as it results in code that is simpler and less error
prone. Multiple tables can be used if it is impractical for the entire
attribute set to live in one place in your code.
*bleprph* uses a single attribute table located in the *gatt\_svr.c*
file, so let's take a look at that now. The attribute table is called
``gatt_svr_svcs``; here are the first several lines from this table:
.. code:: c
static const struct ble_gatt_svc_def gatt_svr_svcs[] = {
{
/*** Service: Security test. */
.type = BLE_GATT_SVC_TYPE_PRIMARY,
.uuid = &gatt_svr_svc_sec_test_uuid.u,
.characteristics = (struct ble_gatt_chr_def[]) { {
/*** Characteristic: Random number generator. */
.uuid = &gatt_svr_chr_sec_test_rand_uuid.u,
.access_cb = gatt_svr_chr_access_sec_test,
.flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_READ_ENC,
}, {
/*** Characteristic: Static value. */
.uuid = gatt_svr_chr_sec_test_static_uuid.u,
.access_cb = gatt_svr_chr_access_sec_test,
.flags = BLE_GATT_CHR_F_READ |
BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_WRITE_ENC,
}, {
// [...]
As you can see, the table is an array of service definitions (
``struct ble_gatt_svc_def``). This code excerpt contains a small part of
the *GAP service*. The GAP service exposes generic information about a
device, such as its name and appearance. Support for the GAP service is
mandatory for all BLE peripherals. Let's now consider the contents of
this table in more detail.
A service definition consists of the following fields:
+----------+------------+----------+
| *Field* | *Meaning* | *Notes* |
+==========+============+==========+
| type | Specifies | Secondar |
| | whether | y |
| | this is a | services |
| | primary or | are not |
| | secondary | very |
| | service. | common. |
| | | When in |
| | | doubt, |
| | | specify |
| | | *BLE\_GA |
| | | TT\_SVC\ |
| | | _TYPE\_P |
| | | RIMARY* |
| | | for new |
| | | services |
| | | . |
+----------+------------+----------+
| uuid128 | The | If the |
| | UUID of | service |
| | this | has a |
| | service. | 16-bit |
| | | UUID, |
| | | you can |
| | | convert |
| | | it to |
| | | its |
| | | correspo |
| | | nding |
| | | 128-bit |
| | | UUID |
| | | with the |
| | | ``BLE_UU |
| | | ID16()`` |
| | | macro. |
+----------+------------+----------+
| characte | The array | |
| ristics | of | |
| | characteri | |
| | stics | |
| | that | |
| | belong to | |
| | this | |
| | service. | |
+----------+------------+----------+
A service is little more than a container of characteristics; the
characteristics themselves are where the real action happens. A
characteristic definition consists of the following fields:
+----------+------------+----------+
| *Field* | *Meaning* | *Notes* |
+==========+============+==========+
| uuid128 | The | If the |
| | UUID of | characte |
| | this | ristic |
| | characteri | has a |
| | stic. | 16-bit |
| | | UUID, |
| | | you can |
| | | convert |
| | | it to |
| | | its |
| | | correspo |
| | | nding |
| | | 128-bit |
| | | UUID |
| | | with the |
| | | ``BLE_UU |
| | | ID16()`` |
| | | macro. |
+----------+------------+----------+
| access\_ | A callback | *For |
| cb | function | reads:* |
| | that gets | this |
| | executed | function |
| | whenever a | generate |
| | peer | s |
| | device | the |
| | accesses | value |
| | this | that |
| | characteri | gets |
| | stic. | sent |
| | | back to |
| | | the |
| | | peer.\ * |
| | | For |
| | | writes:* |
| | | this |
| | | function |
| | | receives |
| | | the |
| | | written |
| | | value as |
| | | an |
| | | argument |
| | | . |
+----------+------------+----------+
| flags | Indicates | The full |
| | which | list of |
| | operations | flags |
| | are | can be |
| | permitted | found |
| | for this | under |
| | characteri | ``ble_ga |
| | stic. | tt_chr_f |
| | The NimBLE | lags`` |
| | stack | in |
| | responds | `net/nim |
| | negatively | ble/host |
| | when a | /include |
| | peer | /host/bl |
| | attempts | e\_gatt. |
| | an | h <https |
| | unsupporte | ://githu |
| | d | b.com/ap |
| | operation. | ache/myn |
| | | ewt-core |
| | | /blob/ma |
| | | ster/net |
| | | /nimble/ |
| | | host/inc |
| | | lude/hos |
| | | t/ble_ga |
| | | tt.h>`__ |
| | | . |
+----------+------------+----------+
The access callback is what implements the characteristic's behavior.
Access callbacks are described in detail in the next section: :doc:`BLE
Peripheral - Characteristic Access <bleprph-chr-access>`.
The service definition array and each characteristic definition array is
terminated with an empty entry, represented with a 0. The below code
listing shows the last service in the array, including terminating zeros
for the characteristic array and service array.
.. code-block:: console
:emphasize-lines: 17,22
{
/*** Service: Security test. */
.type = BLE_GATT_SVC_TYPE_PRIMARY,
.uuid = &gatt_svr_svc_sec_test_uuid.u,
.characteristics = (struct ble_gatt_chr_def[]) { {
/*** Characteristic: Random number generator. */
.uuid = &gatt_svr_chr_sec_test_rand_uuid.u,
.access_cb = gatt_svr_chr_access_sec_test,
.flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_READ_ENC,
}, {
/*** Characteristic: Static value. */
.uuid = &gatt_svr_chr_sec_test_static_uuid.u,
.access_cb = gatt_svr_chr_access_sec_test,
.flags = BLE_GATT_CHR_F_READ |
BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_WRITE_ENC,
}, {
0, /* No more characteristics in this service. */
} },
},
{
0, /* No more services. */
},
Registration function
^^^^^^^^^^^^^^^^^^^^^
After you have created your service table, your app needs to register it
with the NimBLE stack. This is done by calling the following function:
.. code:: c
int
ble_gatts_add_svcs(const struct ble_gatt_svc_def *svcs)
where *svcs* is table of services to register.
The ``ble_gatts_add_svcs()`` function returns 0 on success, or a
*BLE_HS_E[...]* error code on failure.
More detailed information about the registration callback function can
be found in the :doc:`BLE User Guide <../../../network/ble/ble_intro>`
(TBD).
The *bleprph* app registers its services as follows:
.. code:: c
rc = ble_gatts_add_svcs(gatt_svr_svcs);
assert(rc == 0);
which adds services to registration queue. On startup NimBLE host automatically calls ``ble_gatts_start()`` function which makes all registered serivices available to peers.
Descriptors and Included Services
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Your peripheral can also expose descriptors and included services. These
are less common, so they are not covered in this tutorial. For more
information, see the :doc:`BLE User
Guide <../../../network/ble/ble_intro>`.