Creating GATT database
The GATT Database contains several GATT Services where each Service must contain at least one GATT Characteristic.
The Attribute Database contains a collection of attributes. Each attribute has four fields:
The attribute handle – a 2-byte database index, which starts from 0x0001 and increases with each new attribute, not necessarily consecutive; maximum value is 0xFFFF.
The attribute type or UUID – a 2-byte or 16-byte UUID.
The attribute permissions – 1 byte containing access flags; this defines whether the attribute’s value can be read or written and the security requirements for each operation type
The attribute value – an array of maximum 512 bytes.
The ATT does not interpret the UUIDs and values contained in the database. It only deals with data transfer based on the attributes’ handles.
The GATT gives meaning to the attributes based on their UUIDs and groups them into Characteristics and Services.
There are two possible ways of defining the GATT database:
At compile-time (statically) or
At runtime (dynamically)
Creating static GATT database
To define a GATT Database at compile-time, several macros are provided by the GATT_DB API. These macros expand in many different ways at compilation, generating the corresponding Attribute Database on which the Attribute Protocol (ATT) may operate.
This is the default way of defining the database.
The GATT Database definition is written in two files that are required to be added to the application project together with all macro expansion files:
gatt_db.h - contains the actual declaration of Services and Characteristics.
gat_uuid128.h – contains the declaration of Custom UUIDs (16-byte wide); these UUIDs are given a user-friendly name that is used in gatt_db.h file instead of the entire 16-byte sequence.
Declaring custom 128-bit UUIDs
All Custom 128-bit UUIDs are declared in the required file gatt_uuid128.h.
Each line in this file contains a single UUID declaration. The declaration uses the following macro:
UUID128 (name, byte1, byte2, …, byte16)
Thename parameter is the user-friendly handle that references this UUID in the gatt_db.hfile.
The 16 bytes are written in the LSB-first order each one using the 0xZZ format.
Note: On some occasions, it is desired to reuse an 128-bit UUID declared in gatt_uuid128.h. The 16 byte array is available through its friendly name and be accessed by including gatt_db_handles.h in the application. It is strongly advised to use it only in read-only operations. For example:
(gatt_uuid128.h)
UID128(uuid_service_wireless_uart, 0xE0, 0x1C, 0x4B, 0x5E, 0x1E, 0xEB, 0xA1, 0x5C, 0xEE, 0xF4, 0x5E, 0xBA, 0x00, 0x01, 0xFF, 0x01)
(app.c)
#include "gatt_db_handles.h"
........
/* Start Service Discovery*/
BleServDisc_FindService(peerDeviceId, gBleUuidType128_c, (bleUuid_t*) &uuid_service_wireless_uart);
Parent topic:Creating static GATT database
Declaring a service
There are two types of Services:
Primary Services
Secondary Services - these are only to be included by other Primary or Secondary Services
The Service declaration attribute has one of these UUIDs, as defined by the Bluetooth SIG:
0x2800 a.k.a. <<Primary Service>> - for a Primary Service declaration
0x2801 a.k.a. <<Secondary Service>> - for a Secondary Service declaration
The Service declaration attribute permissions are read-only and no authentication required. The Service declaration attribute value contains the Service UUID. The Service Range starts from the Service declaration and ends at the next service declaration. All the Characteristics declared within the Service Range are considered to belong to that Service. For a more comprehensive list of SIG defied UUID values, check ble_sig_defines.h
.
Service declaration macros
The following macros are to be used for declaring a Service:
PRIMARY_SERVICE (name, uuid16)
Most often used.
The name parameter is common to all macros; it is a universal, user-friendly identifier for the generated attribute.
The uuid16 is a 2-byte SIG-defined UUID, written in 0xZZZZ format.
PRIMARY_SERVICE_UUID32 (name, uuid32)
This macro is used for a 4-byte, SIG-defined UUID, written in 0xZZZZZZZZ format.
PRIMARY_SERVICE_UUID128 (name, uuid128)
The uuid128 is the friendly name given to the custom UUID in the gatt_uuid128.h file.
SECONDARY _SERVICE (name, uuid16)
SECONDARY_SERVICE_UUID32 (name, uuid32)
SECONDARY _SERVICE_UUID128 (name, uuid128)
All three are similar to Primary Service declarations.
Parent topic:Declaring a service
Include declaration macros
Secondary Services are meant to be included by other Services, usually by Primary Services. Primary Services may also be included by other Primary Services. The inclusion is done using the Include declaration macro:
INCLUDE (service_name)
The service_name parameter is the friendly name used to declare the Secondary Service.
This macro is used only for Secondary Services with a SIG-defined, 2-byte, Service UUID.
INCLUDE_CUSTOM (service_name)
This macro is used for Secondary Services that have either a 4-byte UUID or a 16-byte UUID.
The effect of the service inclusion is that the including Service is considered to contain all the Characteristics of the included Service.
Parent topic:Declaring a service
Parent topic:Creating static GATT database
Declaring a characteristic
A Characteristic must only be declared inside a Service. It belongs to the most recently declared Service, so the GATT Database must always begin with a Service declaration.
The Characteristic declaration attribute has the following UUID, as defined by the Bluetooth SIG:
0x2803 a.k.a. <<Characteristic>>
The Characteristic declaration attribute value contains:
the Characteristic UUID
the Characteristic Value ’s declaration handle
the Characteristic Properties – Read, Write, Notify, and so on. (1 byte of flags)
The Characteristic Rangestarts from the Characteristic declaration and ends before a new Characteristic or a Service declaration.
After the Characteristic declaration these follow:
A Characteristic Value declaration (mandatory; immediately after the Characteristic declaration).
Zero or more Characteristic Descriptor declarations.
Characteristic declaration macros
The following macros are used to declare Characteristics:
CHARACTERISTIC (name, uuid16, properties)
CHARACTERISTIC_UUID32 (name, uuid32, properties)
CHARACTERISTIC _UUID128 (name, uuid128, properties*)***
See Service declaration for uuidXXX parameter explanation.
The properties parameter is a bit mask. The flags are defined in the gattCharacteristicPropertiesBitFields_t.
Parent topic:Declaring a characteristic
Declaring characteristic values
The Characteristic Value declaration immediately follows the Characteristic declaration and uses one of the following macros:
VALUE (name, uuid16, permissions, valueLength, valueByte1, valueByte2, …)
VALUE_UUID32 (name, uuid32, permissions, valueLength, valueByte1, valueByte2, …)
VALUE _UUID128(name, uuid128, permissions, valueLength, valueByte1, valueByte2, …)
See Declaring a service for description of the uuidXXX parameter.
The permissions parameter is a bit mask, whose flags are defined in gattAttributePermissionsBitFields_t .
The valueLength is the number of bytes to be allocated for the Characteristic Value. After this parameter, exactly [valueLength ] bytes follow in 0xZZ format, representing the initial value of this Characteristic.
These macros are used to declare Characteristic Values of fixed lengths.
Some Characteristics have variable length values. For those, the following macros are used:
VALUE_VARLEN (name, uuid16, permissions, maximumValueLength, initialValueLength, valueByte1, valueByte2, …)
VALUE_UUID32_VARLEN (name, uuid32, permissions, maximumValueLength, initialValueLength, valueByte1, valueByte2, …)
VALUE_UUID128_VARLEN (name, uuid128, permissions, maximumValueLength, initialValueLength, valueByte1, valueByte2, …)
The number of bytes allocated for this Characteristic Value is maximumValueLength.
The number of valueByteXXX parameters shall be equal to initialValueLength.
Obviously, initialValueLength is, at most, equal to maximumValueLength.
Parent topic:Declaring a characteristic
Declaring characteristic descriptors
Characteristic’s Descriptors are declared after the Characteristic Value declaration and before the next Characteristic declaration.
The macros used to declare Characteristic Descriptors are very similar to those used to declare fixed-length Characteristic Values:
DESCRIPTOR (name, uuid16, permissions, descriptorValueLength, descriptorValueByte1, descriptorValueByte2, …)
DESCRIPTOR_UUID32 (name, uuid32, permissions, descriptorValueLength, descriptorValueByte1, descriptorValueByte2, …)
DESCRIPTOR_UUID128(name, uuid128, permissions, descriptorValueLength, descriptorValueByte1, descriptorValueByte2, …)
A special Characteristic Descriptor that is used very often is the Client Characteristic Configuration Descriptor (CCCD). This is the descriptor where clients write some of the bits to activate Server notifications and/or indications. It has a reserved, 2-byte, SIG-defined UUID (0x2902), and its attribute value consists of only 1 byte (out of which 2 bits are used for configuration, the other 6 are reserved).
Because the CCCD appears very often in Characteristic definitions for standard Bluetooth Low Energy profiles, a special macro is used for CCCD declaration:
CCCD (name)
This simple macro is basically equivalent to the following Descriptor declaration:
* DESCRIPTOR \(name, *
*0x2902, *
(gGattAttPermAccessReadable_c
| gGattAttPermAccessWritable_c),
2, 0x00, 0x00)
Parent topic:Declaring a characteristic
Parent topic:Creating static GATT database
Static GATT database definition examples
The GAP Service must be present on any GATT Database. It has the Service UUID equal to 0x1800, <<GAP Service>>, and it contains three read-only Characteristics, no authentication required: *Device Name, Appearance,*and Peripheral Preferred Connection Parameters. These also have well defined UUIDs in the SIG documents.
Most of the demos also include the optional GATT Security Levels characteristic, which defines the highest security requirements of the GATT server when operating in a LE connection.
The definition for this Service is shown here:
**PRIMARY\_SERVICE\(service\_gap, 0x1800\) **
**CHARACTERISTIC\(char\_device\_name, 0x2A00, \(gGattCharPropRead\_c\) \)**
**VALUE\(value\_device\_name, 0x2A00, \(gGattAttPermAccessReadable\_c\), ** **6, “Sensor”\)**
**CHARACTERISTIC\(char\_appearance, 0x2A01, \(gGattCharPropRead\_c\) \)**
**VALUE\(value\_appearance, 0x2A01, \(gGattAttPermAccessReadable\_c\), 2, 0xC2, 0x03\)**
**CHARACTERISTIC\(char\_ppcp, 0x2A04, \(gGattCharPropRead\_c\) \)**
**VALUE\(value\_ppcp, 0x2A04, \(gGattAttPermAccessReadable\_c\), ****8, 0x0A, 0x00, 0x10, 0x00, 0x64, 0x00, 0xE2, 0x04\)**
**CHARACTERISTIC\(char\_security\_levels, gBleSig\_GattSecurityLevels\_d, \(gGattCharPropRead\_c\) \)
VALUE\(value\_security\_levels, gBleSig\_GattSecurityLevels\_d, \(gPermissionFlagReadable\_c\), 2, 0x01, 0x01\)**
Another often encountered Service is the Scan Parameters Service:
**PRIMARY\_SERVICE\(service\_scan\_parameters, 0x1813\)**
**CHARACTERISTIC\(char\_scan\_interval\_window, 0x2A4F, \(gGattCharPropWriteWithoutRsp\_c\) \)**
**VALUE\(value\_scan\_interval\_window, 0x2A4F, \(gGattAttPermAccessWritable\), ** **4, 0x00, 0x00, 0x00, 0x00\)**
**CHARACTERISTIC\(char\_scan\_refresh, 0x2A31, \(gGattCharPropRead\_c \| gGattCharPropNotify\_c\) \)**
**VALUE\(value\_scan\_refresh, 0x2A31, \(gGattAttPermAccessReadable\_c\), 1, 0x00\)** **CCCD\(cccd\_scan\_refresh\)**
Note: All “user-friendly” names given in declarations are statically defined as enum members, numerically equal to the attribute handle of the declaration. This means that one of those names can be used in code wherever an attribute handle is required as a parameter of a function if gatt_db_handles.h is included in the application source file. For example, to write the value of the Scan Refresh Characteristic from the application-level code, use these instructions:
#include "gatt_db_handles.h"
...
uint8_t scan_refresh_value = 0x12;
*GattDb\_WriteAttribute\(char\_scan\_refresh, 1, &scan\_refresh\_value\);*
For static database declarations, the ‘attribute handle’ is equal to the line number in the gatt_fb.h
file, where the attribute is defined.
Parent topic:Creating static GATT database
Parent topic:Creating GATT database
Creating a GATT database dynamically
To define a GATT Database at runtime, the gGattDbDynamic_d macro must be defined in app_preinclude.h with the value equal to 1.
Then, the application must use the APIs provided by the gatt_db_dynamic.h interface to add and remove Services and Characteristics as needed.
See Creating static GATT database for a detailed description of Service and Characteristic parameters.
Memory considerations
The GATT Dynamic database module internally manages the memory allocation for the database. If the gMemManagerLightExtendHeapAreaUsage
define is set to 1
in the desired application, the whole available heap is used. In such as case, the user does not have to allocate space for the dynamic database. If this is not done, the user only needs to make sure that the MinimalHeapSize_c
define is set to a high enough value considering all attributes and attribute values they want to add to the database, as well as other memory requirements the application might have.
Internally, two buffers are used by the dynamic database module: an attribute buffer and a value buffer. The attribute buffer size increases with the addition of each attribute to the database. The value buffer size increases depending on the UUID type and value lengths required by the application. The two buffers start with a minimum size and are reallocated whenever new requests to add entries are received and there is not enough available memory left. If the user removes these entries from the database, the memory reserved for those entries is not freed, but shifted, leaving room for new entries. Thus, an add operation after a remove operation might not necessarily reallocate the buffer if the new entries fit. The two buffers used by the Dynamic database module will not be available to the application until the user releases the database.
Parent topic:Creating a GATT database dynamically
Initialization and release
Before anything can be added to the database, it must be initialized with an empty collection of attributes.
The GattDbDynamic_Init() API is automatically called by the GattDb_Init() implementation provided in the gatt_database.c source file. Application-specific code does not need to call this API again, unless at some point it destroys the database with GattDb_ReleaseDatabase().
Parent topic:Creating a GATT database dynamically
Adding services
The APIs that can be used to add Services are self-explanatory:
GattDbDynamic_AddPrimaryServiceDeclaration
The Service UUID is specified as parameter.
Memory requirements: one entry in the attribute buffer and UUID size in value buffer.
GattDbDynamic_AddSecondaryServiceDeclaration
The Service UUID is specified as parameter.
Memory requirements: one entry in the attribute buffer and UUID size in value buffer.
GattDbDynamic_AddIncludeDeclaration
The Service UUID and handle range are specified as parameters.
Memory requirements: one entry in the attribute buffer and 6 bytes in value buffer.
The functions have an optional out parameter pOutHandle. If its value is not NULL, the execution writes a 16-bit value in the pointed location representing the attribute handle of the added declaration. The application can use this handle as parameter in few GattDbApp APIs or in the Service removal functions.
At least one Service must be added before any Characteristic.
Parent topic:Creating a GATT database dynamically
Adding characteristics and descriptors
The APIs for adding Characteristics and Descriptors are enumerated below:
GattDbDynamic_AddCharacteristicDeclarationAndValue
The Characteristic UUID, properties, access permissions, and initial value are specified as parameters.
GattDbDynamic_AddCharacteristicDeclarationWithUniqueValue
Multiple calls to this API allocate a unique 512-byte value buffer as an optimization for application that deal with large value buffers that do not always need to be stored separately.
GattDbDynamic_AddCharacteristicDescriptor
The Descriptor UUID, access permissions and initial value are specified as parameters.
GattDbDynamic_AddCccd
Shortcut for a CCCD.
Characteristics and descriptors are automatically added at the end of the database. Thus, a service declaration should be followed by all desired characteristic and descriptor definitions before adding a new service to the database.
Parent topic:Creating a GATT database dynamically
Removing services and characteristics
To remove a Service or a Characteristic, the following APIs may be used, both of which only require the declaration handle as parameter:
GattDbDynamic_RemoveService
GattDbDynamic_RemoveCharacteristic
Parent topic:Creating a GATT database dynamically
Parent topic:Creating GATT database
Gatt caching
Service change feature
The service changed feature applies to GATT servers and supports the service changed characteristic, dynamic databases, and handle value indications. The GATT clients that require to be notified for structural modifications on the database should write the CCCD of the Service Changed Characteristic on the server. The value of the Service Changed characteristic is represented by 2 handle values for the handle range affected by the modifications.
The changes that trigger a server database modification are represented by the following API calls:
GattDbDynamic_AddPrimaryServiceDeclaration
GattDbDynamic_AddSecondaryServiceDeclaration
GattDbDynamic_AddIncludeDeclaration
GattDbDynamic_AddCharacteristicDeclarationAndValue
GattDbDynamic_AddCharDescriptor
GattDbDynamic_AddCccd
GattDbDynamic_RemoveService
GattDbDynamic_RemoveCharacteristic
Those GATT server API calls update two internal handles to memorize the minimum and maximum range affected by the change.
After the GATT server database update is done, the application must call the GattDbDynamic_EndDatabaseUpdate()
API. After this, a Service Changed indication is internally sent to each connected peer that has enabled these indications. The indication contains the handle range affected by the change.
. For bonded devices with whom the server is not currently in an active connection, the changes are buffered on the server and the peers are notified upon reconnection.
Parent topic:Gatt caching
Robust caching
Robust caching is a feature where the server sends an error response to the client if the server does not consider the client to be aware of a database structural change. A server supporting robust caching provides the Client Supported Features, Database Hash, and Service Changed characteristics. To indicate support for robust caching, clients should write the robust caching bit (bit 0) of the Client Supported Features characteristic on the server. An example of the GAP service definition for a server with robust caching support is the following:
PRIMARY_SERVICE(service_gatt, gBleSig_GenericAttributeProfile_d)
CHARACTERISTIC(char_service_changed, gBleSig_GattServiceChanged_d, (gGattCharPropIndicate_c))
VALUE(value_service_changed, gBleSig_GattServiceChanged_d, (gPermissionNone_c), 4, 0x01, 0x00, 0xFF, 0xFF)
CCCD(cccd_service_changed)
CHARACTERISTIC(char_client_supported_features, gBleSig_GattClientSupportedFeatures_d, (gGattCharPropRead_c | gGattCharPropWrite_c))
VALUE(value_client_supported_features, gBleSig_GattClientSupportedFeatures_d, (gPermissionFlagReadable_c | gPermissionFlagWritable_c), 8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00)
CHARACTERISTIC(char_database_hash, gBleSig_GattDatabaseHash_d, (gGattCharPropRead_c))
VALUE(value_database_hash, gBleSig_GattDatabaseHash_d, (gPermissionFlagReadable_c), 16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00)
The Client Supported Features characteristic should be declared as an array. The array size is equal to the maximum number of connections, which can be active at one time, so that each possible peer can have its own value. The Server Supported Features characteristic value is automatically written by the Host to indicate robust caching support when appropriate. The Database Hash characteristic value is a 128-bit unsigned integer number where the computed hash value is written.
In order to enable the Robust Caching feature, the gGattCaching_d define should be enabled in the file app_preinclude.h
. In order to enable automatic Host support for Robust Caching, the gGattAutomaticRobustCachingSupport_ddefine should also be enabled. This includes writing the Client Supported Features characteristic, writing the CCCD of the Service Changed characteristic, and reading the Database Hash after reconnecting with a previously bonded peer to check for new changes. If this define is not enabled, then it is up to the application to read and write all necessary characteristic for Robust Caching support.
The client state is kept on the server using the following enum:
typedef enum
{
gGattClientChangeUnaware_c = 0x00U, /*!< Gatt client state */
gGattClientStateChangePending_c = 0x01U, /*!< Gatt client state */
gGattClientChangeAware_c = 0x02U, /*!< Gatt client state */
} gattCachingClientState_c;
The initial state of a client without a trusted relationship is change-aware. The state of a client with a trusted relationship remains unchanged from the previous connection. However, in cases where the database has been updated since the last connection, the initial state is change-unaware. When a database update occurs, all connected clients become change unaware.
If a change-unaware client sends an ATT command, the server ignores it. For ATT requests received from a change-unaware client, the server sends an error response with the error code set to gAttErrCodeDatabaseOutOfSync_c. The server should also not send indications and notifications to change unaware clients, except for the Service Changed indication. The state of a client is verified by the GATT server before executing each command, request or sending any notifications or indications.
The following PDU types are an exception to this rule and do not generate an gAttErrCodeDatabaseOutOfSync_c
error code:
ATT_FIND_INFORMATION_REQ
ATT_FIND_BY_TYPE_VALUE_REQ
ATT_READ_BY_GROUP_TYPE_REQ
ATT_EXECUTE_WRTIE
For a change unaware client to become change aware again, one of the following must happen:
The client receives and confirms a Service Changed Indication.
The server, upon receiving a request from a change unaware client, sends the client a response with the error code set to
Database Out Of Sync
and then the server receives another ATT request from the client.The change unaware client reads the Database Hash characteristic and then the server receives another ATT request from the client.
The function GattDb_ComputeDatabaseHash() is used by the server to compute the hash value and save its value in the database. The computation is done when a read request for the database hash characteristic is first received from a peer GATT client for dynamic databases.
For static databases, hash computation is disabled by default. If you have a static database and want to compute the database hash, then declare the following define to TRUE
in app_preinclude.h
: gGattDbComputeHash_d. By doing this, the hash value is computed during the host initialization. The value is written directly to the database as characteristic and it can be viewed in the memory, as see in the image below. Since static databases do not change in structure over time, this value remains constant, so it can be saved separately and written manually to memory if needed. See Figure 1.
||
|
|
On the client side, the GattClient_GetDatabaseHash() function is used to read the hash value from a peer GATT server. If the gGattAutomaticRobustCachingSupport_c define is enabled, then the following steps are executed automatically:
Writing the Client Supported Features characteristic value to indicate robust caching support – set BIT0 to
1
.Write the CCCD of the Service Changed characteristic.
Read the initial value of the Database Hash characteristic and store it locally.
Otherwise, just the read request for the Database Hash characteristic is sent to the peer.
The following arrays and variables are used for the implementations of the robust caching and service changed features (declared in ble_globals.c
):
/* Service changed indication buffer */
gattHandleRange_t gServiceChangedIndicationStorage[gMaxBondedDevices_c];
/* client saved values for service changed characteristic and CCCD handles */
uint16_t mActiveServiceChangedCharHandle[gAppMaxConnections_c] = {gGattDbInvalidHandle_d};
uint16_t mServiceChangedCharHandle[gMaxBondedDevices_c] = {gGattDbInvalidHandle_d};
uint16_t mActiveServiceChangedCCCDHandle[gAppMaxConnections_c] = {gGattDbInvalidHandle_d};
/* server values for its own service changed characteristic and CCCD handles */
uint16_t mServerServiceChangedCharHandle;
uint16_t mServerServiceChangedCCCDHandle;
/* client state information for bonded and active clients */
gattCachingClientState_c gGattClientState[gMaxBondedDevices_c] = {gGattClientChangeAware_c};
gattCachingClientState_c gGattActiveClientState[gAppMaxConnections_c] = {gGattClientChangeAware_c};
/* Database hash values - the client needs a hash value for each possible peer */
uint8_t mGattActiveServerDatabaseHash[gGattDatabaseHashSize_c * gAppMaxConnections_c] = {0};
uint8_t mGattServerDatabaseHash[gGattDatabaseHashSize_c * gMaxBondedDevices_c] = {0};
/* client supported features handles for active gatt servers */
uint16_t gGattActiveClientSupportedFeaturesHandles[gAppMaxConnections_c] = {gGattDbInvalidHandle_d};
/* client supported features information for bonded gatt clients */
uint8_t gGattClientSupportedFeatures[gMaxBondedDevices_c] = {0U};
/* index of the database hash characteristic in the database */
uint32_t mServerDatabaseHashIndex = gGattDbInvalidHandleIndex_d;
/* index of the client supported features characteristic in the database */
uint32_t mServerClientSupportedFeatureIndex = gGattDbInvalidHandleIndex_d;
It is up to the application to save a local copy of the information from the server’s database and to initiate service discovery only on the first connection or when it is informed of a change by the peer using Service Changed and Robust Caching.
If the gGattAutomaticRobustCachingSupport_c define is not set, it is up to the application to check the Server Supported Features characteristic value on the peer, to write the Client Supported Features characteristic value and to write the Service Changed CCCD.
Two new GATT procedures are introduced as part of the Robust Caching feature. Both should be treated according to the application needs in the GATT procedure callback of the application.
*gGattProcSignalServiceDiscoveryComplete_c –*informs the application that the service discovery procedure has finished after reading the Database Hash value using the read using characteristic UUID procedure. The application procedure callback should call *BleServDisc_Finished()*on this event when robust caching is supported.
*gGattProcUpdateDatabaseCopy_c –*informs the application that its database copy is no longer up to date and service discovery should be reperformed. Used when the client received an error response with the gAttErrCodeDatabaseOutOfSync_c opcode, when the local database hash value is found to be out of sync with the one on the server, or when a service changed indication is received from the server.
If service discovery is performed using our ble_service_discovery module, then the application should wait for the gDiscoveryFinished_c event before initiating its own GATT procedures. The application should also make sure to not initiate a second GATT procedure which requires a response from the peer before receiving a response to the first request it made.
Parent topic:Gatt caching
Parent topic:Creating GATT database