SSブログ

ESP32 の BLEデバイス化にチャレンジ!(3) [Arduino]

今日は久しぶりに時間ができたので、じっくりと ESP32 の BLE について調べることができました。


DSC04860.JPG


なんとなく構造が分かってきたので、少しスケッチを整理してみました。見通しをよくするために "Body Sensor Location" や "Heart Rate Control Point" など、余計な "Characteristics" は省いてしまいました。

#pragma GCC diagnostic push
#pragma GCC diagnostic warning "-fpermissive"

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "esp_system.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "bt.h"
#include "bta_api.h"

#include "esp_gap_ble_api.h"
#include "esp_gatts_api.h"
#include "esp_bt_defs.h"
#include "esp_bt_main.h"
#include "esp_bt_main.h"

#define HRPS_HT_MEAS_MAX_LEN            13

///Attributes State Machine
enum {
    HRS_IDX_SVC,

    HRS_IDX_HR_MEAS_CHAR,
    HRS_IDX_HR_MEAS_VAL,
    HRS_IDX_HR_MEAS_NTF_CFG,

    HRS_IDX_NB,
};

#define ESP_HEART_RATE_APP_ID           0x55
#define SAMPLE_DEVICE_NAME              "ESP_HEART_RATE"
#define SAMPLE_MANUFACTURER_DATA_LEN    17
#define HEART_RATE_SVC_INST_ID          0

uint16_t heart_rate_handle_table[HRS_IDX_NB];

static uint8_t heart_rate_service_uuid[16] = {
    /* LSB <--------------------------------------------------------------------------------> MSB */
    //first uuid, 16bit, [12],[13] is the value
    0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00, 0x80, 0x00, 0x10, 0x00, 0x00, 0x18, 0x0D, 0x00, 0x00,
};

static esp_ble_adv_data_t heart_rate_adv_config = {
    .set_scan_rsp = false,
    .include_name = true,
    .include_txpower = true,
    .min_interval = 0x20,
    .max_interval = 0x40,
    .appearance = 0x00,
    .manufacturer_len = 0, //TEST_MANUFACTURER_DATA_LEN,
    .p_manufacturer_data =  NULL, //&test_manufacturer[0],
    .service_data_len = 0,
    .p_service_data = NULL,
    .service_uuid_len = sizeof(heart_rate_service_uuid),
    .p_service_uuid = heart_rate_service_uuid,
    .flag = (ESP_BLE_ADV_FLAG_GEN_DISC | ESP_BLE_ADV_FLAG_BREDR_NOT_SPT),
};

static esp_ble_adv_params_t heart_rate_adv_params; 

struct gatts_profile_inst {
    esp_gatts_cb_t gatts_cb;
    uint16_t gatts_if;
    uint16_t app_id;
    uint16_t conn_id;
    uint16_t service_handle;
    esp_gatt_srvc_id_t service_id;
    uint16_t char_handle;
    esp_bt_uuid_t char_uuid;
    esp_gatt_perm_t perm;
    esp_gatt_char_prop_t property;
    uint16_t descr_handle;
    esp_bt_uuid_t descr_uuid;
};

static void gatts_profile_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param);

/* One gatt-based profile one app_id and one gatts_if, this array will store the gatts_if returned by ESP_GATTS_REG_EVT */
static struct gatts_profile_inst heart_rate_profile = {
    .gatts_cb = gatts_profile_event_handler,
    .gatts_if = ESP_GATT_IF_NONE, 
};

/// Heart Rate Sensor Service
static const uint16_t heart_rate_svc = ESP_GATT_UUID_HEART_RATE_SVC;

#define CHAR_DECLARATION_SIZE   (sizeof(uint8_t))
static const uint16_t primary_service_uuid = ESP_GATT_UUID_PRI_SERVICE;
static const uint16_t character_declaration_uuid = ESP_GATT_UUID_CHAR_DECLARE;
static const uint16_t character_client_config_uuid = ESP_GATT_UUID_CHAR_CLIENT_CONFIG;
static const uint8_t char_prop_notify = ESP_GATT_CHAR_PROP_BIT_NOTIFY;
static const uint8_t char_prop_read = ESP_GATT_CHAR_PROP_BIT_READ;
static const uint8_t char_prop_read_write = ESP_GATT_CHAR_PROP_BIT_WRITE|ESP_GATT_CHAR_PROP_BIT_READ;

/// Heart Rate Sensor Service - Heart Rate Measurement Characteristic, notify
static const uint16_t heart_rate_meas_uuid = ESP_GATT_HEART_RATE_MEAS;
static const uint8_t heart_measurement_ccc[2] ={ 0x00, 0x00};

static uint8_t hrm_value = 0x00;

/// Full HRS Database Description - Used to add attributes into the database
static const esp_gatts_attr_db_t heart_rate_gatt_db[HRS_IDX_NB] =
{
    // Heart Rate Service Declaration
    [HRS_IDX_SVC]                       =  
    {{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&primary_service_uuid, ESP_GATT_PERM_READ,
      sizeof(uint16_t), sizeof(heart_rate_svc), (uint8_t *)&heart_rate_svc}},

    // Heart Rate Measurement Characteristic Declaration
    [HRS_IDX_HR_MEAS_CHAR]            = 
    {{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_prop_notify}},
      
    // Heart Rate Measurement Characteristic Value
    [HRS_IDX_HR_MEAS_VAL]               =   
    {{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&heart_rate_meas_uuid, ESP_GATT_PERM_READ,
      HRPS_HT_MEAS_MAX_LEN, sizeof(hrm_value), (uint8_t *)&hrm_value}},

    // Heart Rate Measurement Characteristic - Client Characteristic Configuration Descriptor
    [HRS_IDX_HR_MEAS_NTF_CFG]       =    
    {{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&character_client_config_uuid, ESP_GATT_PERM_READ|ESP_GATT_PERM_WRITE,
      sizeof(uint16_t),sizeof(heart_measurement_ccc), (uint8_t *)heart_measurement_ccc}},
};

//////////////////////////////////////////////////////
static void ble_indicate() {
    if (heart_rate_profile.gatts_if == ESP_GATT_IF_NONE) {
        Serial.println("cannot indicate: gatts_if_for_indicate is NONE");
        return;
    }
    hrm_value = random(100);
    Serial.println(hrm_value);

    esp_ble_gatts_send_indicate(heart_rate_profile.gatts_if, heart_rate_profile.conn_id, heart_rate_profile.char_handle, 
        sizeof(hrm_value), (uint8_t *)&hrm_value, true);

}
//////////////////////////////////////////////////////


static 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_DATA_SET_COMPLETE_EVT:
        Serial.println("gap start advertising....");
        esp_ble_gap_start_advertising(&heart_rate_adv_params);
        break;
    case ESP_GAP_BLE_ADV_START_COMPLETE_EVT:
        //advertising start complete event to indicate advertising start successfully or failed
        if (param->adv_start_cmpl.status != ESP_BT_STATUS_SUCCESS) {
            Serial.println("gap advertising failed");
        }
        Serial.println("gap advertising success");
        break;
    default:
        break;
    }
}

static bool bConnected = false;

static void gatts_profile_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:
        Serial.println("gatt server register event");
        esp_ble_gap_set_device_name(SAMPLE_DEVICE_NAME);
        esp_ble_gap_config_adv_data(&heart_rate_adv_config);
        esp_ble_gatts_create_attr_tab(heart_rate_gatt_db, gatts_if, HRS_IDX_NB, HEART_RATE_SVC_INST_ID);
        break;
    case ESP_GATTS_READ_EVT: 
        Serial.println("gatt server read event");
        break;
    case ESP_GATTS_WRITE_EVT: 
        Serial.println("gatt server write event");       
        break;
    case ESP_GATTS_EXEC_WRITE_EVT:
        Serial.println("gatt server exec write event");       
        break;
    case ESP_GATTS_MTU_EVT:
        Serial.println("gatt server mtu event?");       
         break;
   case ESP_GATTS_CONF_EVT:
        Serial.println("gatt server configuration event");       
        break;
   case ESP_GATTS_UNREG_EVT:
        Serial.println("gatt server unregister event");       
        break;
   case ESP_GATTS_DELETE_EVT:
        Serial.println("gatt server delete event");       
        break;
   case ESP_GATTS_START_EVT:
        Serial.println("gatt server start event");       
        break; 
   case ESP_GATTS_STOP_EVT:
        Serial.println("gatt server stop event");       
        break;
   case ESP_GATTS_CONNECT_EVT:
        heart_rate_profile.gatts_if = gatts_if;
        heart_rate_profile.conn_id = param->connect.conn_id;
        heart_rate_profile.char_handle = param->add_char.attr_handle;
        bConnected = true;
        break;
   case ESP_GATTS_DISCONNECT_EVT:
        Serial.println("gatt server disconnect event");     
        heart_rate_profile.gatts_if = ESP_GATT_IF_NONE;
        bConnected = false;
        break;
   case ESP_GATTS_OPEN_EVT:
        Serial.println("gatt server open event");     
        break;
   case ESP_GATTS_CANCEL_OPEN_EVT:
        Serial.println("gatt server cancel open event");     
        break;
   case ESP_GATTS_CLOSE_EVT:
        Serial.println("gatt server close event");     
        break;
   case ESP_GATTS_LISTEN_EVT:
        Serial.println("gatt server listen event");     
        break;
   case ESP_GATTS_CONGEST_EVT:
        Serial.println("gatt server congest event?");     
        break;
   case ESP_GATTS_CREAT_ATTR_TAB_EVT:
        {
            if (param->add_attr_tab.status != ESP_GATT_OK){
                Serial.println("create attribute table failed");
            } else if (param->add_attr_tab.num_handle != HRS_IDX_NB){
                Serial.println("create attribute table abnormally");
            } else {
                Serial.println("gatt server create attribute tab successfully");     
                memcpy(heart_rate_handle_table, param->add_attr_tab.handles, sizeof(heart_rate_handle_table));
                esp_ble_gatts_start_service(heart_rate_handle_table[HRS_IDX_SVC]);
            }
        }
        break;
   default:
        break;
   }
}

static void gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if,  esp_ble_gatts_cb_param_t *param)
{
    /* If event is register event, store the gatts_if for each profile */
    if (event == ESP_GATTS_REG_EVT) {
        if (param->reg.status == ESP_GATT_OK) {
            Serial.println("gatt server register accepted");
            heart_rate_profile.gatts_if = gatts_if;
        } else {
            Serial.println("gatt server register app failed");
            return;
        }
    }
  
    do {
        int idx;
        if (gatts_if == ESP_GATT_IF_NONE || gatts_if == heart_rate_profile.gatts_if) {
            if (heart_rate_profile.gatts_cb) {
                heart_rate_profile.gatts_cb(event, gatts_if, param);
            }
        }
    } while (0);
}

void setup() {

    Serial.begin(115200);

    /* initialize advertising info */
    heart_rate_adv_params.adv_int_min = 0x20;
    heart_rate_adv_params.adv_int_max = 0x40;
    heart_rate_adv_params.adv_type = ADV_TYPE_IND;
    heart_rate_adv_params.own_addr_type = BLE_ADDR_TYPE_PUBLIC;
    heart_rate_adv_params.channel_map = ADV_CHNL_ALL;
    heart_rate_adv_params.adv_filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY;
   
    esp_err_t ret;

    btStart();

    Serial.println("Init bluetooth");
    ret = esp_bluedroid_init();
    if (ret) {
        Serial.println("esp_bluedroid_init failed");
        return;
    }
    ret = esp_bluedroid_enable();
    if (ret) {
        Serial.println("esp_bluedroid_enable failed");
        return;
    }

    esp_ble_gatts_register_callback(gatts_event_handler);
    esp_ble_gap_register_callback(gap_event_handler);
    esp_ble_gatts_app_register(ESP_HEART_RATE_APP_ID);
}

void loop() {
  if (bConnected) {
      ble_indicate();
  } 
  delay(1000);
}



LAPIS Semicondocutor の BLE TOOL で Android スマホから接続してみたら、HRM として認識させることに成功しました!やった。


Screenshot_2017-11-19-00-14-12.png


でも、値を受け取ることはできませんでした。NOTIFY をうまく伝えることができていないようです。あと一息なんだけどなぁ。 orz...


Screenshot_2017-11-19-00-14-48.png


こちらがログになります。

Init bluetooth
gatt server register accepted
gatt server register event
gatt server create attribute tab successfully
gap start advertising....
gatt server start event
gap advertising success
90
85
71
gatt server write event
69
10
97
32
91
62
19
gatt server write event
78
47
83
gatt server disconnect event


どうも Attribute の handle 値が正しく設定されていないようなのですが、どう値を持ってくればよいのかよく分かりません。ESP32 の BLE 関連の情報がほとんどなくしばらく苦戦しそうです。うーむ。
(。-`ω´-)





waves ESP32-DevKitC ESP-WROOM-32 ESP32 DevKitC V2 WiFi BLE 技適取得済 国内発送

waves ESP32-DevKitC ESP-WROOM-32 ESP32 DevKitC V2 WiFi BLE 技適取得済 国内発送

  • 出版社/メーカー: waves(ウェイブス)
  • メディア: エレクトロニクス



waves ESP32 ESP-WROOM-32 WiFi/Bluetoothモジュール(技適取得済み) ブレイクアウトボード付属

waves ESP32 ESP-WROOM-32 WiFi/Bluetoothモジュール(技適取得済み) ブレイクアウトボード付属

  • 出版社/メーカー: waves(ウェイブス)
  • メディア: おもちゃ&ホビー



SparkFun ESP32 Thing Development Workshop (English Edition)

SparkFun ESP32 Thing Development Workshop (English Edition)

  • 出版社/メーカー: PE Press
  • 発売日: 2017/04/09
  • メディア: Kindle版




タグ:ESP32 BLE Arduino
nice!(25)  コメント(2) 
共通テーマ:趣味・カルチャー

nice! 25

コメント 2

ワンモア

こんばんは〜。こういう解析は時間ある日に
じっくりと取り組みのが良いですよね。
細切れの時間だと全然進みません(笑)
by ワンモア (2017-11-19 03:04) 

ys_oota

久しぶりにゆったりと時間が取れたのに、最後まで完結できずに残念。また次の週末に持ち越しです。うまく動くといいけどなー。
(´・ω・`)
by ys_oota (2017-11-21 00:55) 

コメントを書く

お名前:
URL:
コメント:
画像認証:
下の画像に表示されている文字を入力してください。