Ask me anything
Ithy Logo
Sign in to save

Initializing GATT After Starting GAP on ESP32 with ESP-IDF

Comprehensive guide to setting up GATT services post GAP initialization on ESP32

esp32 development board ble setup

Key Takeaways

  • Essential Initialization Steps: Properly initializing NVS, Bluetooth controller, and Bluedroid stack is crucial for successful GATT setup.
  • Callback Registration: Registering GAP and GATT callbacks ensures effective handling of Bluetooth events and interactions.
  • Service and Characteristic Configuration: Defining and creating GATT services and characteristics forms the foundation for BLE data exchange.

Introduction

Setting up the Generic Attribute Profile (GATT) after initializing the Generic Access Profile (GAP) on the ESP32 using the ESP-IDF framework is a fundamental process for enabling Bluetooth Low Energy (BLE) functionalities. This comprehensive guide walks you through each step required to initialize GATT, complete with detailed code examples to facilitate implementation.

Prerequisites

Before diving into the initialization process, ensure that you have the following:

  • ESP32 development board
  • Latest version of ESP-IDF installed
  • Basic understanding of BLE concepts and ESP32 Bluetooth stack
  • Development environment set up with necessary tools (e.g., Visual Studio Code, PlatformIO)

Step-by-Step Initialization of GATT After GAP

1. Initialize Non-Volatile Storage (NVS)

The ESP32 BLE stack relies on NVS for storing BLE configurations persistently. Initializing NVS is the first critical step.

#include "nvs_flash.h"

void initialize_nvs() {
    esp_err_t ret = nvs_flash_init();
    if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
        ESP_ERROR_CHECK(nvs_flash_erase());
        ret = nvs_flash_init();
    }
    ESP_ERROR_CHECK(ret);
}
  

Explanation: This code initializes the NVS. If the NVS partition is truncated or the version is incompatible, it erases and reinitializes the NVS.

2. Initialize the Bluetooth Controller

After NVS, the Bluetooth controller must be initialized and enabled in BLE mode.

#include "esp_bt.h"
#include "esp_bt_main.h"

void initialize_bluetooth_controller() {
    esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT();
    ESP_ERROR_CHECK(esp_bt_controller_init(&bt_cfg));
    ESP_ERROR_CHECK(esp_bt_controller_enable(ESP_BT_MODE_BLE));
}
  

Explanation: This snippet initializes the Bluetooth controller with default configurations and enables it in BLE mode.

3. Initialize the Bluedroid Stack

Bluedroid is the Bluetooth protocol stack used by ESP32. Initializing and enabling it is essential for BLE operations.

#include "esp_bluez.h"

void initialize_bluedroid() {
    ESP_ERROR_CHECK(esp_bluedroid_init());
    ESP_ERROR_CHECK(esp_bluedroid_enable());
}
  

Explanation: This code initializes and enables the Bluedroid stack, making BLE functionalities available.

4. Setting Up GAP Parameters

Configuring GAP parameters involves setting the device name, appearance, and advertising data to make the device discoverable.

#include "esp_gap_ble_api.h"

void set_gap_parameters() {
    // Set the device name
    ESP_ERROR_CHECK(esp_ble_gap_set_device_name("ESP32_GATT_SERVER"));
    
    // Configure advertising data
    esp_ble_adv_data_t adv_data = {
        .set_scan_rsp = false,
        .include_name = true,
        .include_txpower = true,
        .min_interval = 0x20,
        .max_interval = 0x40,
        .appearance = 0x00,
        .manufacturer_len = 0,
        .p_manufacturer_data = NULL,
        .service_data_len = 0,
        .p_service_data = NULL,
        .service_uuid_len = 0,
        .p_service_uuid = NULL,
        .flag = (ESP_BLE_ADV_FLAG_GEN_DISC | ESP_BLE_ADV_FLAG_BREDR_NOT_SPT),
    };
    ESP_ERROR_CHECK(esp_ble_gap_config_adv_data(&adv_data));
}
  

Explanation: This function sets the device name and configures the advertising data to include the device name and transmit power, among other parameters.

5. Register GAP Callback

Registering a GAP callback is vital for handling BLE events such as advertising completion, connection requests, and scanning results.

#include "esp_gap_ble_api.h"

void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) {
    switch (event) {
        case ESP_GAP_BLE_ADV_START_COMPLETE_EVT:
            if (param->adv_start_cmpl.status != ESP_BT_STATUS_SUCCESS) {
                // Advertising start failed
                ESP_LOGE("GAP", "Advertising start failed");
            }
            break;
        // Handle other GAP events as needed
        default:
            break;
    }
}

void register_gap_callback() {
    ESP_ERROR_CHECK(esp_ble_gap_register_callback(gap_event_handler));
}
  

Explanation: The GAP event handler processes various BLE events. In this example, it handles the advertising start completion event.

6. Register GATT Callback

Similar to GAP, registering a GATT callback allows handling GATT-specific events such as service registration and characteristic reads/writes.

#include "esp_gatts_api.h"

void gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param) {
    switch (event) {
        case ESP_GATTS_REG_EVT:
            // GATT server registration event
            ESP_LOGI("GATT", "GATT registered successfully");
            break;
        // Handle other GATT events as needed
        default:
            break;
    }
}

void register_gatt_callback() {
    ESP_ERROR_CHECK(esp_ble_gatts_register_callback(gatts_event_handler));
}
  

Explanation: This function registers the GATT callback handler to manage GATT-related events.

7. Initialize GATT and Register Application

After setting up callbacks, the GATT server needs to be initialized and an application should be registered to manage GATT services and characteristics.

#define GATTS_PROFILE_APP_ID 0

void initialize_gatt() {
    // Register GATT application
    ESP_ERROR_CHECK(esp_ble_gatts_app_register(GATTS_PROFILE_APP_ID));
    
    // Set the local MTU size (optional)
    ESP_ERROR_CHECK(esp_ble_gatt_set_local_mtu(500));
}
  

Explanation: This snippet registers the GATT application and optionally sets the MTU size for data exchange.

8. Define and Create GATT Services and Characteristics

Defining services and characteristics is fundamental for enabling BLE data exchange. Each service can contain multiple characteristics.

#include "esp_gatt_common_api.h"

static const uint16_t GATTS_SERVICE_UUID = 0x1234;
static const uint16_t GATTS_CHAR_UUID = 0x5678;
static const uint16_t GATTS_NUM_HANDLE = 4;

esp_attr_value_t gatts_demo_char_val = {
    .attr_max_len = 512,
    .attr_len     = sizeof(uint8_t),
    .attr_value   = NULL,
};

static const esp_gatts_attr_db_t gatt_db[2] = {
    // Service Declaration
    [0] = {
        {ESP_GATT_AUTO_RSP}, 
        {
            ESP_UUID_LEN_16, 
            (uint8_t*)&primary_service_uuid, 
            ESP_GATT_PERM_READ,
            sizeof(uint16_t), 
            sizeof(GATTS_SERVICE_UUID), 
            (uint8_t*)&GATTS_SERVICE_UUID
        }
    },
    // Characteristic Declaration
    [1] = {
        {ESP_GATT_AUTO_RSP}, 
        {
            ESP_UUID_LEN_16, 
            (uint8_t*)&character_declaration_uuid, 
            ESP_GATT_PERM_READ,
            CHAR_DECLARATION_SIZE, 
            CHAR_DECLARATION_SIZE, 
            (uint8_t*)&char_property
        }
    },
    // Characteristic Value
    [2] = {
        {ESP_GATT_AUTO_RSP}, 
        {
            ESP_UUID_LEN_16, 
            (uint8_t*)&GATTS_CHAR_UUID, 
            ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE,
            GATTS_DEMO_CHAR_VAL_LEN_MAX, 
            sizeof(uint8_t), 
            (uint8_t*)&gatts_demo_char_val
        }
    },
};

void create_gatt_service(esp_gatt_if_t gatts_if) {
    esp_ble_gatts_create_service(gatts_if, &gatt_db[0], GATTS_NUM_HANDLE);
}
  

Explanation: This code defines a GATT service with a unique UUID and a characteristic within that service. The service and characteristic are added to the GATT database and created using the `esp_ble_gatts_create_service` function.

9. Start Advertising

Advertising makes the ESP32 discoverable to other BLE devices, allowing connections and interactions.

#include "esp_gap_ble_api.h"

void start_advertising() {
    esp_ble_adv_params_t adv_params = {
        .adv_int_min        = 0x20,
        .adv_int_max        = 0x40,
        .adv_type           = ADV_TYPE_IND,
        .own_addr_type      = BLE_ADDR_TYPE_PUBLIC,
        .channel_map        = ADV_CHNL_ALL,
        .adv_filter_policy  = ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY,
    };
    ESP_ERROR_CHECK(esp_ble_gap_start_advertising(&adv_params));
}
  

Explanation: This function sets the advertising parameters, such as advertising interval and type, and initiates advertising.

Example Implementation

Below is a minimal working example that brings together all the steps discussed above to initialize GATT after starting GAP on the ESP32.

#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "nvs_flash.h"
#include "esp_bt.h"
#include "esp_bt_main.h"
#include "esp_gap_ble_api.h"
#include "esp_gatt_common_api.h"
#include "esp_gatts_api.h"

#define GATTS_PROFILE_APP_ID 0
#define PROFILE_NUM 1
#define PROFILE_APP_IDX 0

static void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) {
    // Handle GAP events
}

static void gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param) {
    // Handle GATT events
}

void app_main(void) {
    // 1. Initialize NVS
    initialize_nvs();
    
    // 2. Initialize Bluetooth controller
    initialize_bluetooth_controller();
    
    // 3. Initialize Bluedroid stack
    initialize_bluedroid();
    
    // 4. Set GAP parameters
    set_gap_parameters();
    
    // 5. Register GAP callback
    register_gap_callback();
    
    // 6. Register GATT callback
    register_gatt_callback();
    
    // 7. Initialize GATT
    initialize_gatt();
    
    // 8. Define and create GATT services and characteristics
    // This should be handled within the GATT event handler upon registration
}
  

Explanation: This `app_main` function sequentially executes all necessary initialization steps, ensuring that NVS, Bluetooth controller, Bluedroid stack, GAP, and GATT are properly initialized and configured before starting BLE operations.


Conclusion

Initializing GATT after starting GAP on the ESP32 using ESP-IDF involves a series of methodical steps, including initializing NVS, setting up the Bluetooth controller and Bluedroid stack, configuring GAP parameters, and registering the necessary callbacks. By carefully following the outlined steps and ensuring each component is correctly configured, you can establish robust BLE services and characteristics, enabling effective data exchange between your ESP32 device and other BLE-enabled devices.


References


Last updated January 18, 2025

AI-assisted research. Sources, methodology and corrections · Source-checked guides and tools · Explore published answers

Ask Ithy AI