The Bluetooth SDK 2.4 introduced a new feature called Polymorphic GATT which can be used to dynamically show or hide GATT services and characteristics.


This new feature allows customers to create a ‘superset’ GATT database with pre-defined hidden/visible services and characteristics and then alter their visibility on the fly.


How it Works

The visibility of services and characteristics is controlled through the use of GATT capabilities. The usage of capabilities and their syntax in the GATT xml file is fully described in 'UG118: Blue Gecko Bluetooth Profile Toolkit Developer's Guide', specifically in sections 2.3, 2.4.1 and 2.5.1 with a usage example in section 2.6. It's important that you read through these sections to get a good grasp of the visibility and inheritance rules.


In summary each service/characteristic can declare a number of capabilities and the state of the capabilities (enable/disable) determines the visibility of those services/characteristics as a bit-wise OR operation e.g. the service/characteristic is visible when at least one of its capabilities is enabled and it’s not visible when all of its the capabilities are disabled.


Note: If certain services/characteristics are meant to be always visible then one good approach is to have one capability that is declared by those services/characteristics which is enabled by default and untouched by the application code.


Using VGE

The Simplicity Studio VGE (Visual GATT Editor) already supports the polymorphic GATT database and it allows to declare capabilities for the whole GATT database as well as subsets for each of the services and characteristics.


You must always start by declaring the GATT-level capabilities and define their default value. That is done by selecting "Custom BLE GATT" and adding the capabilities in "Capability declaration". To add a capability just press the '+' on the right-hand side and then change the capability name and default value.



Once those capabilities are added they become available on each of the services and characteristics. They can be added through the '+' control but this time you'll be shown the list of capabilities declared at the GATT-level where to pick from.




The attached example shows how the capabilities can be used to change the visibility of services and characteristics to a GATT client. The usage of the example is shown in the below video.


Note: Changing the capabilities state should not be done during a connection as that can cause misbehavior. The safest way is to change the capabilities when no devices are connected.


Declaring the Capabilities in the GATT

In this example there are 5 capabilities which are all enabled by default. The GATT was created using VGE which generates the gatt.xml and gatt_db.c/.h files used to build the project.


Looking into the gatt.xml file we can see all the capabilities declaration as they are explained in UG118.

  <gatt generic_attribute_service="true" header="gatt_db.h" name="Custom BLE GATT" out="gatt_db.c" prefix="gattdb_">
      <capability enable="true">ota</capability>
      <capability enable="true">temp_measure</capability>
      <capability enable="true">temp_type</capability>
      <capability enable="true">interm_temp</capability>
      <capability enable="true">meas_interv</capability>


The first 2 services in the GATT database, Generic Access and Device Information, don’t declare any capabilities so they will inherit all the capabilities from the GATT as per the inheritance rules described in UG118 section 2.3.1. This means that if all the capabilities are disabled then these services and all their characteristics will be hidden from a GATT client.


Then the OTA service declared the ota capability which means that the state of this capability alone will determine the visibility of the OTA service.

    <service advertise="false" name="Silicon Labs OTA" requirement="mandatory" sourceId="com.silabs.service.ota" type="primary" uuid="1D14D6EE-FD63-4FA1-BFA4-8F47B42119F0">
      <informativeText>Abstract: The Silicon Labs OTA Service enables over-the-air firmware update of the device. </informativeText>


The Health Thermometer service declares the remaining 4 capabilities. Then each of the 4 characteristics under this service declares only one of those capabilities, allowing their visibility to be individually controlled.

<service advertise="false" id="health_thermometer" name="Health Thermometer" requirement="mandatory" sourceId="org.bluetooth.service.health_thermometer" type="primary" uuid="1809">
      <informativeText>Abstract:  The Health Thermometer service exposes temperature and other data from a thermometer intended for healthcare and fitness applications.  Summary:  The Health Thermometer service is instantiated as a Primary Service. There are one or more instantiations of the Health Thermometer service per device claiming conformance with this specification.  </informativeText>


Enabling/Disabling Capabilities

The capabilities are enabled/disabled with the API command cmd_gatt_server_set_capabilities(caps, reserved) where caps is the bit flags of each capability which should be set to 1 if the capability is to be enabled or 0 if it's to be disabled.


The auto-generated gatt_db.h contains the bit flag value for each of the capabilities.

typedef enum
    ota                            = 0x0001,
    temp_measure                   = 0x0002,
    temp_type                      = 0x0004,
    interm_temp                    = 0x0008,
    meas_interv                    = 0x0010,
    bg_gattdb_data_all_caps = 0x001f
} bg_gattdb_data_cap_t;


If for example we would want to enable ota and temp_type and disable all other capabilities the command call would look like this: cmd_gatt_server_set_capabilities(ota | temp_type, 0);


In this example we declare a variable to hold the state of all the capabilities at any given time (uint8_t capabilities). The enumerated bit flags are not being used but instead an index (uint8_t capability_selected) keeps track of which capability is selected in the display and changes the correct bit in capabilities according to the button presses (each button press will toggle the capability between enable and disable, which in practice is setting and clearing the corresponding pin in capabilities variable).

void handle_button_change(uint8_t pin)
	if(pin == 6)
		// If PB0 was pressed change the selected capability
		if(capability_selected == 5) capability_selected = 0; // wrap around
	} else {
		// If PB1 was pressed swap the capability status: enabled->disabled and vice-versa
		if(capabilities & (1<<capability_selected)) {
			capabilities &= ~(1<<capability_selected);
		} else {
			capabilities |= (1<<capability_selected);
		// API commands cannot be called from within interrupt context, send external signal to be handled by the stack


Because API commands cannot be called from within interrupt context when a capability status is changed we send an external signal to the stack so that it can be handled in the main loop.

      case gecko_evt_system_external_signal_id:
    	  // The capability status was changed
      	  if(evt->data.evt_system_external_signal.extsignals == CAPABILITIES_CHANGED) {


The stack monitors the local database change status and manages the service changed indications for a GATT client that has enabled the indication configuration of the Service Changed characteristic. The Service Changed characteristic is part of the Generic Attribute service and it can be added to the GATT by adding generic_attribute_service="true" to the <gatt> element (as shown in the first gatt.xml snippet in this article).

  • Knowledge Base Articles
  • Bluetooth Low Energy
  • Bluetooth Classic