GATT server scripting guide
1. Introduction
This guide demonstrates the creation and usage of custom user-created GATT services and characteristics. For a simplified version of the GATT console, please refer to: GATT server console service
API: GATT server API
This functionality is accessible with the ***_02_003 device firmware and 2.3.0 Bluetooth firmware versions or higher.
1.2 Features
Ability to dynamically create GATT services and GATT characteristics.
Variable-length characteristic value for GATT client
readoperations.Capability to easily identify characteristics during updates of characteristic values and upon data reception from GATT clients, using both UUIDs and user references.
Ability to automatically send notifications to the subscribers when the characteristic value changes.
1.3 Limitations
The current implementation of a GATT server has the following limitations:
Up to 10 services are available for creation
Up to 13 characteristics are available for creation, of which up to 7 can be subscribable.
Up to 40 GATT attributes can be used.
Up to 200 bytes can be used for characteristic value/buffer update.
Up to 220 bytes can be received from the GATT client.
Memory pool for all characteristics is limited to 1112 bytes.
These limitations also apply to system services if they are used. For more information refer to: https://docs.xirgoglobal.com/space/SD/408748171/GATT+server+system+services
2. Steps to create a working GATT server
2.1 Initialising Bluetooth and GATT server
This step is needed to initialise Bluetooth and GATT callbacks.
#include <io>
#include <read>
#include <float>
#include <string>
#include <core>
#include <write>
#include <define>
#include <socket>
#include <bluetooth>
main()
{
bt_init();
bt_gatt_server_init();
for (;;)
{
Delay(100);
}
}
2.2 Creating services and characteristics
Upon initialization, GATT services and characteristics may be registered in accordance with the GATT server architecture: initially, a service must be registered, followed by the registration of all characteristics associated with that service. Subsequently, additional services and their corresponding characteristics can be registered. It is important to note that only 128-bit (16-byte) UUIDs are supported.
The initial step involves registering a service, as characteristics cannot be registered independently.
To create a GATT service, call bt_gatt_service_create(uuid[]) function with the custom UUID buffer.
To create a GATT characteristic, call bt_gatt_chrc_create(uuid[], properties, chrc_val_size, user_ref). The UUID argument refers to the characteristic's assigned UUID. properties indicate available operations for that characteristic to the GATT client. The chrc_val_size parameter allocates memory for characteristics possessing the read attribute; a value of 0 is permissible only when this property is not used. user_ref helps to identify the characteristic when updating its value or receiving data from the GATT client. For more info refer to https://it.confluence.xirgo.com/wiki/spaces/MT/pages/679510071.
GATT CCC descriptors are added automatically if the characteristic has a GATT_CHRC_PROP_NOTIFY property.
In the following example, two services are created: the first service includes a characteristic with the read property and a 10-byte buffer. The second service comprises one characteristic that exclusively sends notifications to the GATT client, while the other characteristic possesses the write property and solely receives data from the GATT client to the script callback.
The function bt_gatt_server_start() must be called to register the services and ensure their visibility to the GATT client.
#include <read>
#include <float>
#include <string>
#include <core>
#include <write>
#include <define>
#include <socket>
#include <bluetooth>
new svc_uuid_1[UUID_LENGTH_128_BIT] = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00];
new chrc_uuid_11[UUID_LENGTH_128_BIT] = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01];
new chrc_ref_11 = 11;
new svc_uuid_2[UUID_LENGTH_128_BIT] = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00];
new chrc_uuid_21[UUID_LENGTH_128_BIT] = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x01];
new chrc_uuid_22[UUID_LENGTH_128_BIT] = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02];
new chrc_ref_22 = 22;
main()
{
bt_init();
bt_gatt_server_init();
bt_gatt_service_create(svc_uuid_1);
bt_gatt_chrc_create(chrc_uuid_11, GATT_CHRC_PROP_READ, 10, chrc_ref_11);
bt_gatt_service_create(svc_uuid_2);
bt_gatt_chrc_create(chrc_uuid_21, GATT_CHRC_PROP_NOTIFY, 0, 0);
bt_gatt_chrc_create(chrc_uuid_22, GATT_CHRC_PROP_WRITE, 0, chrc_ref_22);
for(;;)
{
Delay(100);
}
}This code results in the following GATT server architecture:
2.3 Starting the GATT server
After all services and characteristics are created, gatt_server_start() must be called. This function registers all script services and makes them visible to the GATT client.
Note: To make the GATT server visible to other devices, advertising must be started either in the device settings under GATT server settings, or using the script advertisement example below.
Note: If GATT client connected before the bt_gatt_server_start() function is executed, it must use service discovery to acquire the newly registered services and characteristics.
#include <io>
#include <read>
#include <float>
#include <string>
#include <core>
#include <write>
#include <define>
#include <socket>
#include <bluetooth>
new svc_uuid_1[UUID_LENGTH_128_BIT] = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00];
new chrc_uuid_11[UUID_LENGTH_128_BIT] = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01];
new chrc_ref_11 = 11;
new svc_uuid_2[UUID_LENGTH_128_BIT] = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00];
new chrc_uuid_21[UUID_LENGTH_128_BIT] = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x01];
new chrc_uuid_22[UUID_LENGTH_128_BIT] = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02];
new chrc_ref_22 = 22;
main()
{
bt_init();
bt_gatt_server_init();
bt_gatt_service_create(svc_uuid_1);
bt_gatt_chrc_create(chrc_uuid_11, GATT_CHRC_PROP_READ, 10, chrc_ref_11);
bt_gatt_service_create(svc_uuid_2);
bt_gatt_chrc_create(chrc_uuid_21, GATT_CHRC_PROP_NOTIFY, 0, 0);
bt_gatt_chrc_create(chrc_uuid_22, GATT_CHRC_PROP_WRITE, 0, chrc_ref_22);
bt_gatt_server_start();
for(;;)
{
Delay(100);
}
}2.4 Enabling security (optional)
To enable level 4 GATT connection security with data encryption, a 6-digit passkey must be set. There are two ways to set it:
GATT server settings
In GATT server settings, ensure that “Enabled” is selected in the “Passkey enable”.
Enter the desired passkey in the field below.
Script
If the GATT server passkey needs to be set or modified in a script, untick the “Passkey enable” option to prevent the settings passkey from being applied.
Use
bt_gatt_server_passkey_set()function to set the passkey in the script.
Using bt_gatt_server_passkey_set() overrides the previous passkey both from the script and the settings.
#include <io>
#include <read>
#include <float>
#include <string>
#include <core>
#include <write>
#include <define>
#include <socket>
#include <bluetooth>
new svc_uuid_1[UUID_LENGTH_128_BIT] = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00];
new chrc_uuid_11[UUID_LENGTH_128_BIT] = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01];
new chrc_ref_11 = 11;
new svc_uuid_2[UUID_LENGTH_128_BIT] = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00];
new chrc_uuid_21[UUID_LENGTH_128_BIT] = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x01];
new chrc_uuid_22[UUID_LENGTH_128_BIT] = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02];
new chrc_ref_22 = 22;
main()
{
bt_init();
bt_gatt_server_init();
bt_gatt_server_passkey_set(123456);
bt_gatt_service_create(svc_uuid_1);
bt_gatt_chrc_create(chrc_uuid_11, GATT_CHRC_PROP_READ, 10, chrc_ref_11);
bt_gatt_service_create(svc_uuid_2);
bt_gatt_chrc_create(chrc_uuid_21, GATT_CHRC_PROP_NOTIFY, 0, 0);
bt_gatt_chrc_create(chrc_uuid_22, GATT_CHRC_PROP_WRITE, 0, chrc_ref_22);
bt_gatt_server_start();
for(;;)
{
Delay(100);
}
}Utilizing a passkey does not inherently ensure security. It is advisable to avoid transmitting sensitive data via Bluetooth unless supplementary security measures are implemented to protect the data.
2.5 Setting up advertisement
The connectable advertisement can be set up in two ways. When using a system service, the device automatically advertises its name in the scan response packet. If GATT settings or system services are not used, or custom advertisement data is needed, this can be configured via the script.
Using GATT server settings
Enable a system service.
In the “Advertised device name” field enter device name.
This advertisement starts before bt_init() function is executed successfully.
Device name is advertised in the scan response packet by using “Complete local name” (0x09) data type.
Using script functions
Set the desired advertisement data by using
bt_adv_conn_data_set().Start the advertisement by using
bt_adv_conn_start().
#include <io>
#include <read>
#include <float>
#include <string>
#include <core>
#include <write>
#include <define>
#include <socket>
#include <bluetooth>
new svc_uuid_1[UUID_LENGTH_128_BIT] = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00];
new chrc_uuid_11[UUID_LENGTH_128_BIT] = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01];
new chrc_ref_11 = 11;
new svc_uuid_2[UUID_LENGTH_128_BIT] = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00];
new chrc_uuid_21[UUID_LENGTH_128_BIT] = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x01];
new chrc_uuid_22[UUID_LENGTH_128_BIT] = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02];
new chrc_ref_22 = 22;
new adv_data[] = [0x03, 0x09, 0x58, 0x47];
main()
{
bt_init();
bt_gatt_server_init();
bt_gatt_server_passkey_set(123456);
bt_gatt_service_create(svc_uuid_1);
bt_gatt_chrc_create(chrc_uuid_11, GATT_CHRC_PROP_READ, 10, chrc_ref_11);
bt_gatt_service_create(svc_uuid_2);
bt_gatt_chrc_create(chrc_uuid_21, GATT_CHRC_PROP_NOTIFY, 0, 0);
bt_gatt_chrc_create(chrc_uuid_22, GATT_CHRC_PROP_WRITE, 0, chrc_ref_22);
bt_gatt_server_start();
bt_adv_conn_data_set(adv_data, sizeof(adv_data));
bt_adv_conn_start();
for(;;)
{
Delay(100);
}
}bt_adv_conn_data_set() function sets data in the advertising indication packet. It does not change the scan response packet (device name set in GATT server settings).
2.6 Operating the server
There are a few commands possible when the server is running:
Characteristic value can be updated. For this, two characteristic identification ways can be used:
bt_gatt_chrc_update_by_uuid(uuid[], data[], data_length)- Uses the full UUID for the characteristic identification, updates value from thedatabuffer, limited with thedata_lengthargument.bt_gatt_chrc_buf_update_by_ref(user_ref, data[], data_length)- Uses the user reference assigned in thebt_gatt_chrc_create()for characteristic identification, updates value from thedatabuffer, limited with thedata_lengthargument.
If a GATT client is subscribed to the characteristic, it will also receive a notification about the characteristic value update.
Utilize the function
bt_gatt_server_receive()to obtain data from the GATT client. Upon the client writing data to a characteristic, aBT_GATT_SERVER_RXcallback is invoked, enabling the script to access the transmitted data.
It is recommended to update the MTU right after GATT connection to send and receive larger data packets.
#include <io>
#include <read>
#include <float>
#include <string>
#include <core>
#include <write>
#include <define>
#include <socket>
#include <bluetooth>
new svc_uuid_1[UUID_LENGTH_128_BIT] = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00];
new chrc_uuid_11[UUID_LENGTH_128_BIT] = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01];
new identification_data[10] = [0x05, 0x00, 0x56, 0x05, 0x95, 0xAC, 0xE4, 0xFC, 0x23, 0xA0];
new chrc_ref_11 = 11;
new svc_uuid_2[UUID_LENGTH_128_BIT] = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00];
new chrc_uuid_21[UUID_LENGTH_128_BIT] = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x01];
new chrc_21_data[40];
new chrc_uuid_22[UUID_LENGTH_128_BIT] = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02];
new chrc_ref_22 = 22;
new gatt_server_buffer[100];
forward public callback (event);
public callback (event)
{
switch(event)
{
case BT_GATT_SERVER_RX:
{
new data_len = bt_gatt_server_receive(gatt_server_buffer);
if (gatt_server_buffer[GATT_RX_HEADER_USER_REF_POS] == chrc_ref_22)
{
handle_ref_22_msg(data_len);
}
}
}
}
handle_ref_22_msg(message_length)
{
/* Received data into characteristic reference 22 code start */
/* Received data into characteristic reference 22 code end */
}
main()
{
bt_init();
bt_gatt_server_init();
bt_gatt_service_create(svc_uuid_1);
bt_gatt_chrc_create(chrc_uuid_11, GATT_CHRC_PROP_READ, 10, chrc_ref_11);
bt_gatt_chrc_update_by_ref(chrc_ref_11, identification_data, sizeof(identification_data));
bt_gatt_service_create(svc_uuid_2);
bt_gatt_chrc_create(chrc_uuid_21, GATT_CHRC_PROP_NOTIFY, 0, 0);
bt_gatt_chrc_create(chrc_uuid_22, GATT_CHRC_PROP_WRITE, 0, chrc_ref_22);
bt_gatt_server_start();
for(;;)
{
Delay(5000);
snprintf(chrc_21_data, sizeof(chrc_21_data), "Ticks:%d\r\nVCC:%d", get_val(Sensor_Ticks), get_val(SENSOR_VCC));
bt_gatt_chrc_update_by_uuid(chrc_uuid_21, chrc_21_data, strlen(chrc_21_data));
}
}