5
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

nRF52でBLEデバイスを開発する(5)GATT/Characteristicの追加

Last updated at Posted at 2019-10-22

この記事は、nRF52でBLEデバイスを開発する(4)サービスを追加するの続きです。

はじめに

 今回はGATTとCharacteristicを追加します。

GATTの追加

 以下の呼び出しで、UUIDを指定してサービスハンドルを取得します。

static uint16_t service_handle;
    ble_uuid_t            ble_uuid;

    ble_uuid.type = uuid_type;
    ble_uuid.uuid = AB_BLE_UUID_SERVICE;

    err_code = sd_ble_gatts_service_add(BLE_GATTS_SRVC_TYPE_PRIMARY, &ble_uuid, &service_handle);
    APP_ERROR_CHECK(err_code);

 nRF ConnectでCONNECTすると、指定のUUIDでGATTサービスが出ていることが分かります。Characteristicを追加していないので、This service is empty.となっています。

image.png

Characteristicの追加

 以下の呼び出しでCharacteristicを追加します。
 三つ分のUUIDとハンドルを用意します。

#define AB_BLE_UUID_COMMAND_CHAR  0xef70
#define AB_BLE_UUID_RESPONSE_CHAR 0xef71
#define AB_BLE_UUID_DATA_CHAR   0xef72

static ble_gatts_char_handles_t    command_char_handles;
static ble_gatts_char_handles_t    response_char_handles;
static ble_gatts_char_handles_t    data_char_handles;
    ble_add_char_params_t add_char_params;

    // Add Command characteristic.
    memset(&add_char_params, 0, sizeof(add_char_params));
    add_char_params.uuid              = AB_BLE_UUID_COMMAND_CHAR;
    add_char_params.uuid_type         = uuid_type;
    add_char_params.init_len          = sizeof(uint8_t);
    add_char_params.max_len           = AB_BLE_MAX_DATA_LEN;
    add_char_params.is_var_len        = true;
    add_char_params.char_props.write_wo_resp  = 1;

    add_char_params.write_access      = SEC_OPEN;

    err_code = characteristic_add(service_handle,
                                  &add_char_params,
                                  &command_char_handles);
    APP_ERROR_CHECK(err_code);

 これが下りのコマンド用。

 通知用は、パラメータが変わります。

    add_char_params.char_props.read   = 1;
    add_char_params.char_props.notify = 1;

    add_char_params.read_access       = SEC_OPEN;
    add_char_params.cccd_write_access = SEC_OPEN;

 
image.png

 これでデータの送受信チャネルが開きました。

データ受信のイベントハンドラ

 サンプルに入っているble_lbs.c/hを元に作り込んでいきます。
 まず受信ハンドラの割り込みレベル。

#define AB_BLE_OBSERVER_PRIO 2

 関連構造体とオブザーバー定義のためのマクロ。

#define AB_BLE_SERVICE_DEF(_name)                                                                 \
static ab_ble_t _name;                                                                            \
NRF_SDH_BLE_OBSERVER(_name ## _obs,                                                               \
                     AB_BLE_OBSERVER_PRIO,                                                    \
                     ab_ble_on_ble_evt, &_name)

構造体。

typedef struct ab_ble_s
{
    uint8_t                     uuid_type;
    uint16_t                    service_handle;
    ble_gatts_char_handles_t    command_char_handles;
    ble_gatts_char_handles_t    response_char_handles;
    ble_gatts_char_handles_t    data_char_handles;
    ab_ble_command_write_handler_t command_write_handler;
    ab_ble_tx_complete_handler_t tx_complete_handler;
} ab_ble_t; 

 受信ハンドラと送信完了ハンドラ。

typedef void (*ab_ble_command_write_handler_t) (const uint8_t *commands, uint8_t length);
typedef void (*ab_ble_tx_complete_handler_t)(void);

static void on_write(ab_ble_t * p_abs, ble_evt_t const * p_ble_evt)
{
    ble_gatts_evt_write_t const * p_evt_write = &p_ble_evt->evt.gatts_evt.params.write;

    if (p_evt_write->handle == p_abs->command_char_handles.value_handle)
    {
        p_abs->command_write_handler(p_evt_write->data , p_evt_write->len );
    }
}


void ab_ble_on_ble_evt(ble_evt_t const * p_ble_evt, void * p_context)
{
    ab_ble_t * p_abs = (ab_ble_t *)p_context;

    switch (p_ble_evt->header.evt_id)
    {
        case BLE_GATTS_EVT_WRITE:
            on_write(p_abs, p_ble_evt);
            break;

        case BLE_GATTS_EVT_HVN_TX_COMPLETE:
            p_abs->tx_complete_handler();
            break;

       default:
            // No implementation needed.
            break;
    }
}

 これらをこの一行でm_absという変数に定義します。

AB_BLE_SERVICE_DEF(m_abs);

 この後は、m_absという変数に関連するデータを代入します。

 NRF_SDH_BLE_OBSERVER()マクロでハンドラab_ble_on_ble_evt()が関連付けられます。ここから、データ受信時にonWrite()、送信完了イベントでtx_complete_handler()に分岐します。onWriteの中では、データ送信元を識別して、さらに適切なハンドラへと分岐を行います。

void ble_command_write_handler(const uint8_t *commands, uint8_t length)
{
  NRF_LOG_HEXDUMP_INFO(commands, length);
}


void ble_tx_complete_handler(void)
{
  NRF_LOG_INFO("TX COMPLETED.");
}

    m_abs.command_write_handler = ble_command_write_handler;
    m_abs.tx_complete_handler = ble_tx_complete_handler;

 それぞれ、上記の様なハンドラを用意して受信します。

 試してみましょう。nRF-ConnectでAuqaBlueデバイスに接続し、command用のCharacteristicを開きます。
image.png
 ここの赤枠のアイコンを押すと、書き込み画面になります。
image.png
 ダイアログのデフォルトはバイト配列モードなので偶数個の数値を入れて、SENDボタンを押します。nRF-Connect上で書き込めたことが分かります。
image.png
 SESのログ画面に以下のログが出ます。

<info> app: Buttonless DFU Application started.
<info> app:  12 34 56               |.4V    

データの送信

 データの送信には、sd_ble_gatts_hvx()APIを使用します。
 以下の様な関数を用意しておき、

uint32_t ble_service_send_response(uint16_t conn_handle, ab_ble_t * p_abs, const uint8_t* data , uint16_t len )
{
    ble_gatts_hvx_params_t params;

    memset(&params, 0, sizeof(params));
    params.type   = BLE_GATT_HVX_NOTIFICATION;
    params.handle = p_abs->response_char_handles.value_handle;
    params.p_data = data;
    params.p_len  = &len;

    return sd_ble_gatts_hvx(conn_handle, &params);
}

 以下の様に呼び出します。ここでは、受信したデータをそのままエコーバックします。

void ble_command_write_handler(const uint8_t *commands, uint8_t length)
{
  NRF_LOG_HEXDUMP_INFO(commands, length);

  ble_service_send_response(m_conn_handle, &m_abs , commands, length );
}

 先ほどと同様にAquaBlueに接続し、response用Characteristicの赤枠部分をクリックして通知を有効にしてから、0x123456を書き込みます。
image.png
 エコーバックされていることが確認できます。
 SESのログは以下の様になります。

<info> app: Buttonless DFU Application started.
<info> app:  12 34 56               |.4V     
<info> app: TX COMPLETED.

 送信完了イベントが上がっていることが分かります。

 今回は以上となります。

 ソース一式はいつものようにgithub(https://github.com/jiro-aqua/nrf52-ota-example )においてあります。

5
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?