BLEモジュールnRF52シリーズにおけるCentralサービスを実装する仕組み

# 記事の概要

Nordic社製のBLEモジュールのCentralサービスを作成します。
サービス名はorange_cとします。

実際に作成したプロジェクトは以下を参照ください。
https://github.com/matsuikosuke/nRF52_Central_and_Peripheral

# サービスの実体
instance定義用のマクロを作成します。

```C:orange_service_c.h
#define BLE_ORANGE_C_DEF(_name)                                                                        \
static ble_orange_c_t _name;                                                                           \
NRF_SDH_BLE_OBSERVER(_name ## _obs,                                                                 \
                     BLE_ORANGE_C_BLE_OBSERVER_PRIO,                                                   \
                     ble_orange_c_on_ble_evt, &_name)
```

複数のCentralサービスを使用する場合のマクロも作成しておきます。

```C:orange_service_c.h
#define BLE_ORANGE_C_ARRAY_DEF(_name, _cnt)                                                            \
static ble_orange_c_t _name[_cnt];                                                                     \
NRF_SDH_BLE_OBSERVERS(_name ## _obs,                                                                \
                      BLE_ORANGE_C_BLE_OBSERVER_PRIO,                                                  \
                      ble_orange_c_on_ble_evt, &_name, _cnt)
```

# UUID
UUIDを定義します。
ベースになるUUID、サービスUUID、サービスに付随するCharacteristicのUUIDを作成します。
例えばCharacteristicのUUIDはNotify用、Write用、read用などそれぞれの目的に合わせて使い分けます。

ベースUUIDのxxxxの部分にサービスUUIDやCharacteristicのUUIDを代入したものが使用されます。

```C:orange_service_c.h
//BASE-UUID:4ab4xxxx-65bc-126e-3b8c-8345de91b0d3
#define ORANGE_UUID_BASE {0xd3, 0xb0, 0x91, 0xde, 0x45, 0x83, 0x8c, 0x3b, \
                              0x6e, 0x12, 0xbc, 0x65, 0x00, 0x00, 0xb4, 0x4a}
#define ORANGE_UUID_SERVICE               0xABC0
#define ORANGE_READ_UUID_CHAR            0xABC1  //Read
#define ORANGE_INDICATION_UUID_CHAR       0xABC2  //Indication
#define ORANGE_NOTIFICATION_UUID_CHAR     0xABC3  //Notification
#define ORANGE_WRITE_UUID_CHAR            0xABC4  //Write
```

# BLEイベントタイプの構造体
サービスに関するイベントを定義します。
BLE接続確立時のイベント、Peripheral側からデータ受信時（Notification、Indicationなど）のイベントの構造体を作成しておく必要があります。

```C:orange_service_c.h
typedef enum
{
    BLE_ORANGE_C_EVT_DISCOVERY_COMPLETE = 1,  /**< Event indicating that the ORANGE Service was discovered at the peer. */
    BLE_ORANGE_C_EVT_BUTTON_NOTIFICATION,      /**< Event indicating that a notification of the LED Button Button characteristic was received from the peer. */
    BLE_ORANGE_C_EVT_NOTIFICATION,       /**< Event indicating that a notification of the ORANGE characteristic has been received from the peer. */
    BLE_ORANGE_C_EVT_INDICATION,          /**< Event indicating that a indication of the ORANGE characteristic has been received from the peer. */
    BLE_ORANGE_C_EVT_READ
} ble_orange_c_evt_type_t;
```

# サービスに関するハンドラの構造体
notification、notificationのCCCDコールバック、indication、notifyのCCCDコールバック、writeのようなCentralサービスに関するハンドラの構造体を作成します。

```C:orange_service_c.h
typedef struct
{
    uint16_t orange_notify_cccd_handle;  /**< Handle of the CCCD of the Notify characteristic. */
    uint16_t orange_notify_handle;       /**< Handle of the Notify characteristic as provided by the SoftDevice. */
    uint16_t orange_write_handle;          /**< Handle of the Write characteristic as provided by the SoftDevice. */
    uint16_t orange_indicate_cccd_handle;  /**< Handle of the CCCD of the Indicate characteristic. */
    uint16_t orange_indicate_handle;       /**< Handle of the Indicate characteristic as provided by the SoftDevice. */
    uint16_t orange_read_handle;
} orange_db_t;
```

CCCDコールバックについては以下を参照してください。
[CCCDコールバック関数の設定](https://qiita.com/Kosuke_Matsui/items/1cb4b60cbc89562c0d7e#cccd%E3%82%B3%E3%83%BC%E3%83%AB%E3%83%90%E3%83%83%E3%82%AF%E9%96%A2%E6%95%B0%E3%81%AE%E8%A8%AD%E5%AE%9A)

# BLEイベントの構造体
BLEイベントの構造体を作成します。
イベントタイプ、接続ハンドラ、イベントハンドラから成る構造体を作成します。

```C:orange_service_c.h
typedef struct
{
    ble_orange_c_evt_type_t evt_type;    /**< Type of the event. */
    uint16_t             conn_handle; /**< Connection handle on which the event occured.*/
    union
    {
        ble_button_t button;          /**< Button value received. This is filled if the evt_type is @ref BLE_ORANGE_C_EVT_BUTTON_NOTIFICATION. */
        orange_db_t     peer_db;         /**< Handles related to the ORANGE Service found on the peer device. This is filled if the evt_type is @ref BLE_ORANGE_C_EVT_DISCOVERY_COMPLETE.*/
    } params;
} ble_orange_c_evt_t;
```

# BLEベントに対する割り込み関数の定義
BLEイベントによる割り込み処理の関数を定義します。

```C:orange_service_c.h
/**@brief   Event handler type.
 *
 * @details This is the type of the event handler that is to be provided by the application
 *          of this module in order to receive events.
 */
typedef void (* ble_orange_c_evt_handler_t) (ble_orange_c_t * p_ble_orange_c, ble_orange_c_evt_t * p_evt);
```

# Centralサービスの構造体
Centralサービスに関する構造体を作成します。

```C:orange_service_c.h
// Forward declaration of the ble_orange_c_t type.
typedef struct ble_orange_c_s ble_orange_c_t;

/**@brief Orange Client structure. */
struct ble_orange_c_s
{
    uint16_t                     conn_handle;     /**< Connection handle as provided by the SoftDevice. */
    orange_db_t                  peer_orange_db;  /**< Handles related to ORANGE on the peer. */
    ble_orange_c_evt_handler_t   evt_handler;     /**< Application event handler to be called when there is an event related to the Orange service. */
    ble_srv_error_handler_t      error_handler;   /**< Function to be called in case of an error. */
    uint8_t                      uuid_type;       /**< UUID type. */
    nrf_ble_gq_t                 * p_gatt_queue;  /**< Pointer to the BLE GATT Queue instance. */
};
```

# Centralサービス初期化の構造体
Centralサービス初期設定の構造体を作成します。

```C:orange_service_c.h
/**@brief Orange Client initialization structure. */
typedef struct
{
    ble_orange_c_evt_handler_t   evt_handler;   /**< Event handler to be called by the Orange Client module when there is an event related to the ORANGE Service. */
    nrf_ble_gq_t            * p_gatt_queue;  /**< Pointer to the BLE GATT Queue instance. */
    ble_srv_error_handler_t   error_handler; /**< Function to be called in case of an error. */
} ble_orange_c_init_t;
```

# Centaralサービス初期化処理
各種ハンドラの設定、UUIDの設定を行います

```C:orange_service_c.c
uint32_t ble_orange_c_init(ble_orange_c_t * p_ble_orange_c, ble_orange_c_init_t * p_ble_orange_c_init)
{
    uint32_t      err_code;
    ble_uuid_t    orange_uuid;
    ble_uuid128_t orange_base_uuid = {ORANGE_UUID_BASE};

    VERIFY_PARAM_NOT_NULL(p_ble_orange_c);
    VERIFY_PARAM_NOT_NULL(p_ble_orange_c_init);
    VERIFY_PARAM_NOT_NULL(p_ble_orange_c_init->evt_handler);
    VERIFY_PARAM_NOT_NULL(p_ble_orange_c_init->p_gatt_queue);

    p_ble_orange_c->conn_handle                    = BLE_CONN_HANDLE_INVALID;
    p_ble_orange_c->evt_handler                    = p_ble_orange_c_init->evt_handler;
    p_ble_orange_c->p_gatt_queue                   = p_ble_orange_c_init->p_gatt_queue;
    p_ble_orange_c->error_handler                  = p_ble_orange_c_init->error_handler;

    err_code = sd_ble_uuid_vs_add(&orange_base_uuid, &p_ble_orange_c->uuid_type);
    if (err_code != NRF_SUCCESS)
    {
        return err_code;
    }
    VERIFY_SUCCESS(err_code);

    orange_uuid.type = p_ble_orange_c->uuid_type;
    orange_uuid.uuid = ORANGE_UUID_SERVICE;

    return ble_db_discovery_evt_register(&orange_uuid);
}
```

# GATTエラー割り込み処理


```C:orange_service_c.c
static void gatt_error_handler(uint32_t   nrf_error,
                               void     * p_ctx,
                               uint16_t   conn_handle)
{
    ble_orange_c_t * p_ble_orange_c = (ble_orange_c_t *)p_ctx;

    NRF_LOG_DEBUG("A GATT Client error has occurred on conn_handle: 0X%X", conn_handle);

    if (p_ble_orange_c->error_handler != NULL)
    {
        p_ble_orange_c->error_handler(nrf_error);
    }
}
```

# BLEイベント処理
BLEイベント処理です。
BLEイベント割り込み処理`ble_evt_handler`から呼び出されます。
`p_ble_evt->header.evt_id`がBLEデータ受信イベント`BLE_GATTC_EVT_HVX`かBLE切断イベント`BLE_GAP_EVT_DISCONNECTED`や`BLE_GATTC_EVT_READ_RSP`かを判定して分岐し、イベントに対応する処理を実行します。

```C:orange_service_c.c
void ble_orange_c_on_ble_evt(ble_evt_t const * p_ble_evt, void * p_context)
{
    if ((p_context == NULL) || (p_ble_evt == NULL))
    {
        return;
    }

    ble_orange_c_t * p_ble_orange_c = (ble_orange_c_t *)p_context;

    switch (p_ble_evt->header.evt_id)
    {
        case BLE_GATTC_EVT_HVX:
            on_hvx(p_ble_orange_c, p_ble_evt);
            break;

        case BLE_GATTC_EVT_READ_RSP:
            on_read_rsp(p_ble_orange_c, p_ble_evt);
            break;

        case BLE_GAP_EVT_DISCONNECTED:
            on_disconnected(p_ble_orange_c, p_ble_evt);
            break;

        default:
            break;
    }
}
```

## BLE_GATTC_EVT_HVX：BLEデータ受信イベント処理
Notification、Indicationを受信した時に行う処理です。
必須の処理に加えて、アプリケーションの仕様に基づいて処理を追加ください。

```C:orange_service_c.c
static void on_hvx(ble_orange_c_t * p_ble_orange_c, ble_evt_t const * p_ble_evt)
{
    // Check if the event is on the link for this instance.
    if (p_ble_orange_c->conn_handle != p_ble_evt->evt.gattc_evt.conn_handle)
    {
        return;
    }
    // Check if this is a SmartAgri notification.
    if (p_ble_evt->evt.gattc_evt.params.hvx.handle == p_ble_orange_c->peer_orange_db.orange_notify_handle)
    {
            ble_orange_c_evt_t ble_orange_c_evt;
            uint32_t        index = 0;

            ble_orange_c_evt.evt_type                   = BLE_ORANGE_C_EVT_NOTIFICATION;
            ble_orange_c_evt.conn_handle                = p_ble_orange_c->conn_handle;  //By seeing this information, it is determined which device the transmission is from.

            for (int i = 0; i < BLE_NOTIFICATION_DATA_LENGTH; i ++)
            {
                if (index >= p_ble_evt->evt.gattc_evt.params.hvx.len)
                {
                    break;
                }
                notification_buf[i] = p_ble_evt->evt.gattc_evt.params.hvx.data[index];
                index += 1;
            }

            p_ble_orange_c->evt_handler(p_ble_orange_c, &ble_orange_c_evt);
    }
    // Check if this is a SmartAgri indication.
    if (p_ble_evt->evt.gattc_evt.params.hvx.handle == p_ble_orange_c->peer_orange_db.orange_indicate_handle)
    {
            ble_orange_c_evt_t ble_orange_c_evt;
            uint32_t        index = 0;

            ble_orange_c_evt.evt_type                   = BLE_ORANGE_C_EVT_INDICATION;
            ble_orange_c_evt.conn_handle                = p_ble_orange_c->conn_handle;  //By seeing this information, it is determined which device the transmission is from.

            for (int i = 0; i < BLE_INDICATION_DATA_LENGTH; i ++)
            {
                if (index >= p_ble_evt->evt.gattc_evt.params.hvx.len)
                {
                    break;
                }
                indication_buf[i] = p_ble_evt->evt.gattc_evt.params.hvx.data[index];
                index += 1;
            }

            // In case of Indicate, you have to add the following function that returns an ACK response
            sd_ble_gattc_hv_confirm(ble_orange_c_evt.conn_handle,  p_ble_orange_c->peer_orange_db.orange_indicate_handle);

            p_ble_orange_c->evt_handler(p_ble_orange_c, &ble_orange_c_evt);
    }
}
```

この処理の内容について以下に説明します。

### 接続ハンドラの確認

```C
    // Check if the event is on the link for this instance.
    if (p_ble_orange_c->conn_handle != p_ble_evt->evt.gattc_evt.conn_handle)
    {
        return;
    }
```

`p_ble_evt->evt.gattc_evt.conn_handle`が`p_ble_orange_c->conn_handle`と一致しているか確認します。
複数のCentralサービスを実装している場合、`p_ble_evt->evt.gattc_evt.conn_handle`には他のサービスの接続ハンドラが格納されているかもしれません。
その場合はOrangeサービスを適用していはいけないので処理を終了します。

### p_ble_evt->evt.gattc_evt.params.hvx.handleの確認
`p_ble_evt->evt.gattc_evt.params.hvx.handle`がサービスハンドラ構造体`orange_db_t`のどれに対応するか確認します。
ただし`hvx.handle``には受信に関するイベントしかないので、今回はNotificationもしくはIndicationに関するイベントである`orange_notify_handle`か`orange_indicate_handle`のどちらかになります。

### 受信データの確認
`p_ble_evt->evt.gattc_evt.params.hvx.len`には受信データのサイズが格納されています。
`p_ble_evt->evt.gattc_evt.params.hvx.data[]`には受信データが格納されています。

割り込み処理はなるべく短時間で済ませたいので、データをバッファに一時退避させてから、mainループ内で受信データの解析処理をした方がいいと考えて、受信データを`indication_buf`に格納しています。

### 受信タイプの保存と受信割り込み処理の実行

`ble_orange_c_evt.evt_type`にイベントタイプを保存します。
`ble_orange_c_evt.conn_handle`には`p_ble_orange_c->conn_handle`を保存します。

これらを用いてイベントに対応する割り込み処理を呼び出します。

```C
p_ble_orange_c->evt_handler(p_ble_orange_c, &ble_orange_c_evt);
```

これにより割り込み処理`orange_c_evt_handler`において、BLEイベント`ble_orange_c_evt_type_t`に対応する処理が実行されます。
今回の場合はBLEイベント`BLE_ORANGE_C_EVT_NOTIFICATION`もしくは`BLE_ORANGE_C_EVT_INDICATION`が使用されます。

### Indication ACK応答
Indication受信時には、CentralからPeripheralに対してACK応答を送信しないといけません。
以下の処理をそれを実行しています。

```C
// In case of Indicate, you have to add the following function that returns an ACK response
sd_ble_gattc_hv_confirm(ble_orange_c_evt.conn_handle,  p_ble_orange_c->peer_orange_db.orange_indicate_handle);

```

## BLE_GAP_EVT_DISCONNECTED：BLE切断イベント処理
BLE切断時に行う処理です。
各種ハンドラを初期化します。

```C:orange_service_c.c
static void on_disconnected(ble_orange_c_t * p_ble_orange_c, ble_evt_t const * p_ble_evt)
{
    if (p_ble_orange_c->conn_handle == p_ble_evt->evt.gap_evt.conn_handle)
    {
        p_ble_orange_c->conn_handle                    = BLE_CONN_HANDLE_INVALID;
        p_ble_orange_c->peer_orange_db.orange_notify_cccd_handle = BLE_GATT_HANDLE_INVALID;
        p_ble_orange_c->peer_orange_db.orange_notify_handle      = BLE_GATT_HANDLE_INVALID;
        p_ble_orange_c->peer_orange_db.orange_write_handle         = BLE_GATT_HANDLE_INVALID;
        p_ble_orange_c->peer_orange_db.orange_indicate_cccd_handle = BLE_GATT_HANDLE_INVALID;
        p_ble_orange_c->peer_orange_db.orange_indicate_handle      = BLE_GATT_HANDLE_INVALID;
    }
}
```


## BLE_GATTC_EVT_READ_RSP：Read受信処理
Read要求を実行することでPeripheralからReadデータが送信されます。
`BLE_GATTC_EVT_READ_RSP`は、そのデータを受信時に発生するイベントです。

```C:orange_service_c.c
static void on_read_rsp(ble_orange_c_t *p_ble_orange_c, ble_evt_t const *p_ble_evt)
{
    ble_gattc_evt_read_rsp_t const *p_response = &p_ble_evt->evt.gattc_evt.params.read_rsp;
    ble_orange_c_evt_t ble_orange_c_evt;

    // Check if the event is on the link for this instance and the event handler is present.
    if ((p_ble_orange_c->evt_handler == NULL) ||
        (p_ble_orange_c->conn_handle != p_ble_evt->evt.gattc_evt.conn_handle))
    {
        return;
    }

    if (p_ble_evt->evt.gattc_evt.gatt_status == BLE_GATT_STATUS_SUCCESS)
    {
        if (p_response->handle == p_ble_orange_c->peer_orange_db.orange_read_handle)
        {
            ble_orange_c_evt.evt_type = BLE_ORANGE_C_EVT_READ;
            ble_orange_c_evt.conn_handle = p_ble_orange_c->conn_handle;
            NRF_LOG_INFO("Received read data %lu of length %u", * (uint32_t *) p_response->data, p_response->len);

            for(int i=0; i<p_response->len; i++)
            {
                central_read_buf[i] = p_response->data[i];
            }

            p_ble_orange_c->evt_handler(p_ble_orange_c, &ble_orange_c_evt);
        }
    }
}
```

# Write送信処理
CentralからPeripheralへデータをWrite送信する処理です。
この関数はstatusに格納したデータを送信します。

```C:orange_service_c.c
uint32_t ble_orange_status_send(ble_orange_c_t * p_ble_orange_c, uint8_t status)
{
    VERIFY_PARAM_NOT_NULL(p_ble_orange_c);

    if (p_ble_orange_c->conn_handle == BLE_CONN_HANDLE_INVALID)
    {
        return NRF_ERROR_INVALID_STATE;
    }

    NRF_LOG_DEBUG("Writing orange status 0x%x", status);

    nrf_ble_gq_req_t write_req;

    memset(&write_req, 0, sizeof(nrf_ble_gq_req_t));

    write_req.type                        = NRF_BLE_GQ_REQ_GATTC_WRITE;
    write_req.error_handler.cb            = gatt_error_handler;
    write_req.error_handler.p_ctx         = p_ble_orange_c;
    write_req.params.gattc_write.handle   = p_ble_orange_c->peer_orange_db.orange_write_handle;
    write_req.params.gattc_write.len      = sizeof(status);
    write_req.params.gattc_write.p_value  = &status;
    write_req.params.gattc_write.offset   = 0;
    write_req.params.gattc_write.write_op = BLE_GATT_OP_WRITE_CMD; 

    return nrf_ble_gq_item_add(p_ble_orange_c->p_gatt_queue, &write_req, p_ble_orange_c->conn_handle);
}
```

# Read要求
Read要求`ble_orange_read_request`を実行することで、PeripheralからReadデータが返ってきます。
Readデータ受信時には`ble_orange_c_on_ble_evt`割り込み処理においてイベント`BLE_GATTC_EVT_READ_RSP`が呼び出されます。

```C:orange_service_c.c
uint32_t ble_orange_read_request(ble_orange_c_t * p_ble_orange_c)
{
    VERIFY_PARAM_NOT_NULL(p_ble_orange_c);

    if (p_ble_orange_c->conn_handle == BLE_CONN_HANDLE_INVALID)
    {
        return NRF_ERROR_INVALID_STATE;
    }

    NRF_LOG_DEBUG("Read orange");

    nrf_ble_gq_req_t read_req;

    memset(&read_req, 0, sizeof(nrf_ble_gq_req_t));

    read_req.type                        = NRF_BLE_GQ_REQ_GATTC_READ;
    read_req.error_handler.cb            = gatt_error_handler;
    read_req.error_handler.p_ctx         = p_ble_orange_c;
    read_req.params.gattc_read.handle   = p_ble_orange_c->peer_orange_db.orange_read_handle;

    return nrf_ble_gq_item_add(p_ble_orange_c->p_gatt_queue, &read_req, p_ble_orange_c->conn_handle);
}
```


# Discoveryイベント処理
Discoveryイベント処理においては、CentralサービスとのBLE接続確立後、Characteristicに対応するハンドラを設定します。

`p_evt->params.discovered_db.char_count`にはCentralサービスに含まれるCharacteristic数が格納されています。
それらのCharacteristicのUUID`p_char->characteristic.uuid.uuid`を１つずつ確認し、Characteristicに対応するハンドルを設定します。

またconn_handle以外のハンドラが既に設定済みでないかを確認し、設定済みなら既存の設定`evt.params.peer_db`を代入します。

```C:orange_service_c.c
void ble_orange_on_db_disc_evt(ble_orange_c_t * p_ble_orange_c, ble_db_discovery_evt_t const * p_evt)
{
    // Check if the Test Service was discovered.
    if (p_evt->evt_type == BLE_DB_DISCOVERY_COMPLETE &&
        p_evt->params.discovered_db.srv_uuid.uuid == ORANGE_UUID_SERVICE &&
        p_evt->params.discovered_db.srv_uuid.type == p_ble_orange_c->uuid_type)
    {
        ble_orange_c_evt_t evt;

        evt.evt_type    = BLE_ORANGE_C_EVT_DISCOVERY_COMPLETE;
        evt.conn_handle = p_evt->conn_handle;

        for (uint32_t i = 0; i < p_evt->params.discovered_db.char_count; i++)
        {
            const ble_gatt_db_char_t * p_char = &(p_evt->params.discovered_db.charateristics[i]);
            switch (p_char->characteristic.uuid.uuid)
            {
                case ORANGE_WRITE_UUID_CHAR:
                    evt.params.peer_db.orange_write_handle = p_char->characteristic.handle_value;
                    break;

                case ORANGE_NOTIFICATION_UUID_CHAR:
                    evt.params.peer_db.orange_notify_handle      = p_char->characteristic.handle_value;
                    evt.params.peer_db.orange_notify_cccd_handle = p_char->cccd_handle;
                    break;

                case ORANGE_INDICATION_UUID_CHAR:
                    evt.params.peer_db.orange_indicate_handle      = p_char->characteristic.handle_value;
                    evt.params.peer_db.orange_indicate_cccd_handle = p_char->cccd_handle;
                    break;

                default:
                    break;
            }
        }

        NRF_LOG_DEBUG("Orange Service discovered at peer.");
        //If the instance was assigned prior to db_discovery, assign the db_handles
        if (p_ble_orange_c->conn_handle != BLE_CONN_HANDLE_INVALID)
        {
            if ((p_ble_orange_c->peer_orange_db.orange_write_handle         == BLE_GATT_HANDLE_INVALID)&&
                (p_ble_orange_c->peer_orange_db.orange_notify_handle      == BLE_GATT_HANDLE_INVALID)&&
                (p_ble_orange_c->peer_orange_db.orange_notify_cccd_handle == BLE_GATT_HANDLE_INVALID)&&
                (p_ble_orange_c->peer_orange_db.orange_indicate_handle      == BLE_GATT_HANDLE_INVALID)&&
                (p_ble_orange_c->peer_orange_db.orange_indicate_cccd_handle == BLE_GATT_HANDLE_INVALID))
            {
                p_ble_orange_c->peer_orange_db = evt.params.peer_db;
            }
        }

        p_ble_orange_c->evt_handler(p_ble_orange_c, &evt);

    }
}
```

# CCCDコールバック設定処理
CCCDコールバック設定処理です。
BLEイベント割り込み処理`orange_c_evt_handler`の`BLE_ORANGE_C_EVT_DISCOVERY_COMPLETE`イベント発生時に実行します。

```C:orange_service_c.c
static uint32_t cccd_configure_notify(ble_orange_c_t * p_ble_orange_c, bool enable)
{
    NRF_LOG_DEBUG("Configuring CCCD. CCCD Handle = %d, Connection Handle = %d",
                  p_ble_orange_c->peer_orange_db.orange_notify_cccd_handle,
                  p_ble_orange_c->conn_handle);

    nrf_ble_gq_req_t cccd_req;
    uint16_t         cccd_val = enable ? BLE_GATT_HVX_NOTIFICATION : 0;
    uint8_t          cccd[WRITE_MESSAGE_LENGTH];

    cccd[0] = LSB_16(cccd_val);
    cccd[1] = MSB_16(cccd_val);

    cccd_req.type                        = NRF_BLE_GQ_REQ_GATTC_WRITE;
    cccd_req.error_handler.cb            = gatt_error_handler;
    cccd_req.error_handler.p_ctx         = p_ble_orange_c;
    cccd_req.params.gattc_write.handle   = p_ble_orange_c->peer_orange_db.orange_notify_cccd_handle;
    cccd_req.params.gattc_write.len      = WRITE_MESSAGE_LENGTH;
    cccd_req.params.gattc_write.offset   = 0;
    cccd_req.params.gattc_write.p_value  = cccd;
    cccd_req.params.gattc_write.write_op = BLE_GATT_OP_WRITE_REQ;

    return nrf_ble_gq_item_add(p_ble_orange_c->p_gatt_queue, &cccd_req, p_ble_orange_c->conn_handle);
}

static uint32_t cccd_configure_indicate(ble_orange_c_t * p_ble_orange_c, bool enable)
{
    NRF_LOG_DEBUG("Configuring CCCD. CCCD Handle = %d, Connection Handle = %d",
                  p_ble_orange_c->peer_orange_db.orange_indicate_cccd_handle,
                  p_ble_orange_c->conn_handle);

    nrf_ble_gq_req_t cccd_req;
    uint16_t         cccd_val = enable ? BLE_GATT_HVX_INDICATION : 0;
    uint8_t          cccd[WRITE_MESSAGE_LENGTH];

    cccd[0] = LSB_16(cccd_val);
    cccd[1] = MSB_16(cccd_val);

    cccd_req.type                        = NRF_BLE_GQ_REQ_GATTC_WRITE;
    cccd_req.error_handler.cb            = gatt_error_handler;
    cccd_req.error_handler.p_ctx         = p_ble_orange_c;
    cccd_req.params.gattc_write.handle   = p_ble_orange_c->peer_orange_db.orange_indicate_cccd_handle;
    cccd_req.params.gattc_write.len      = WRITE_MESSAGE_LENGTH;
    cccd_req.params.gattc_write.offset   = 0;
    cccd_req.params.gattc_write.p_value  = cccd;
    cccd_req.params.gattc_write.write_op = BLE_GATT_OP_WRITE_REQ;

    return nrf_ble_gq_item_add(p_ble_orange_c->p_gatt_queue, &cccd_req, p_ble_orange_c->conn_handle);
}

uint32_t ble_orange_c_orange_notification_enable(ble_orange_c_t * p_ble_orange_c)
{
    VERIFY_PARAM_NOT_NULL(p_ble_orange_c);

    if (p_ble_orange_c->conn_handle == BLE_CONN_HANDLE_INVALID)
    {
        return NRF_ERROR_INVALID_STATE;
    }

    return cccd_configure_notify(p_ble_orange_c, true);
}

uint32_t ble_orange_c_orange_indication_enable(ble_orange_c_t * p_ble_orange_c)
{
    VERIFY_PARAM_NOT_NULL(p_ble_orange_c);

    if (p_ble_orange_c->conn_handle == BLE_CONN_HANDLE_INVALID)
    {
        return NRF_ERROR_INVALID_STATE;
    }

    return cccd_configure_indicate(p_ble_orange_c, true);
}
```

# BLEハンドラ登録処理
BLEハンドラ登録処理です。BLEイベント割り込み処理`orange_c_evt_handler`の`BLE_ORANGE_C_EVT_DISCOVERY_COMPLETE`イベント発生時に実行します。

```C:orange_service_c.c
uint32_t ble_orange_c_handles_assign(ble_orange_c_t    * p_ble_orange_c,
                                  uint16_t         conn_handle,
                                  const orange_db_t * p_peer_handles)
{
    VERIFY_PARAM_NOT_NULL(p_ble_orange_c);

    p_ble_orange_c->conn_handle = conn_handle;
    if (p_peer_handles != NULL)
    {
        p_ble_orange_c->peer_orange_db = *p_peer_handles;
    }
    return nrf_ble_gq_conn_handle_register(p_ble_orange_c->p_gatt_queue, conn_handle);
}
```

# 参照
- [BLEモジュールnRF52シリーズにおけるCentral機能とPeripheral機能の両方を実装する仕組み](https://qiita.com/Kosuke_Matsui/items/13da1b5df710b4b0a3ab)
- [BLEモジュールnRF52シリーズにおけるCentralサービスを実装する仕組み](https://qiita.com/Kosuke_Matsui/private/f0ae02aa1fdc5494c332)
- [誰でもすぐにBLE通信機器が作れるnRF52用サンプルプログラム](https://qiita.com/Kosuke_Matsui/items/999ad6d1463dbad914d3)