]> granicus.if.org Git - esp-idf/commitdiff
component/bt: add example for ble spp client
authorxiewenxiang <xiewenxiang@espressif.com>
Wed, 23 Aug 2017 09:40:54 +0000 (17:40 +0800)
committerxiewenxiang <xiewenxiang@espressif.com>
Fri, 25 Aug 2017 01:52:24 +0000 (09:52 +0800)
 - this demo needs to work with the spp server demo

examples/bluetooth/ble_spp_client/Makefile [new file with mode: 0644]
examples/bluetooth/ble_spp_client/README.rst [new file with mode: 0644]
examples/bluetooth/ble_spp_client/main/component.mk [new file with mode: 0644]
examples/bluetooth/ble_spp_client/main/spp_client_demo.c [new file with mode: 0644]
examples/bluetooth/ble_spp_client/sdkconfig.defaults [new file with mode: 0644]

diff --git a/examples/bluetooth/ble_spp_client/Makefile b/examples/bluetooth/ble_spp_client/Makefile
new file mode 100644 (file)
index 0000000..9628803
--- /dev/null
@@ -0,0 +1,10 @@
+#
+# This is a project Makefile. It is assumed the directory this Makefile resides in is a
+# project subdirectory.
+#
+
+PROJECT_NAME := spp_client_demo
+
+COMPONENT_ADD_INCLUDEDIRS := components/include
+
+include $(IDF_PATH)/make/project.mk
diff --git a/examples/bluetooth/ble_spp_client/README.rst b/examples/bluetooth/ble_spp_client/README.rst
new file mode 100644 (file)
index 0000000..b0d2494
--- /dev/null
@@ -0,0 +1,3 @@
+ESP-IDF SPP GATT CLIENT demo
+========================
+
diff --git a/examples/bluetooth/ble_spp_client/main/component.mk b/examples/bluetooth/ble_spp_client/main/component.mk
new file mode 100644 (file)
index 0000000..a98f634
--- /dev/null
@@ -0,0 +1,4 @@
+#
+# "main" pseudo-component makefile.
+#
+# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)
diff --git a/examples/bluetooth/ble_spp_client/main/spp_client_demo.c b/examples/bluetooth/ble_spp_client/main/spp_client_demo.c
new file mode 100644 (file)
index 0000000..627abf6
--- /dev/null
@@ -0,0 +1,887 @@
+// Copyright 2015-2017 Espressif Systems (Shanghai) PTE LTD
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+
+
+/****************************************************************************
+*
+* This file is for ble spp client demo.
+*
+****************************************************************************/
+
+#include <stdint.h>
+#include <string.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include "controller.h"
+#include "driver/uart.h"
+
+#include "bt.h"
+#include "nvs_flash.h"
+#include "esp_bt_device.h"
+#include "esp_gap_ble_api.h"
+#include "esp_gattc_api.h"
+#include "esp_gatt_defs.h"
+#include "esp_bt_main.h"
+
+#define GATTC_TAG                   "GATTC_SPP_DEMO"
+#define PROFILE_NUM                 1
+#define PROFILE_APP_ID              0
+#define BT_BD_ADDR_STR              "%02x:%02x:%02x:%02x:%02x:%02x"
+#define BT_BD_ADDR_HEX(addr)        addr[0],addr[1],addr[2],addr[3],addr[4],addr[5]
+
+#define SPP_SRV_INDEX               1
+#define SPP_DATA_RECV_CHAR_INDEX    1
+#define SPP_DATA_NTFY_CHAR_INDEX    2
+#define SPP_CMD_CHAR_INDEX          3
+#define SPP_STATUS_CHAR_INDEX       4
+#define SPP_HEARTBEAT_CHAR_INDEX    5
+#define SPP_NO_DESC                 0
+#define SPP_IS_DESC                 1
+
+//#define SUPPORT_HEARTBEAT
+//#define DEBUG_MODE
+
+typedef struct spp_ble_gattc_char_descr {
+    int char_descr_index;
+    uint16_t conn_id;
+    esp_gatt_id_t descr_id;
+    struct spp_ble_gattc_char_descr *next;
+} spp_ble_gattc_char_descr_t;
+
+typedef struct spp_ble_gattc_char {
+    int srv_index;
+    int index;
+    uint16_t conn_id;
+    esp_gatt_srvc_id_t srvc_id;
+    esp_gatt_id_t char_id;
+    esp_gatt_char_prop_t char_prop;
+    spp_ble_gattc_char_descr_t * char_descr;
+    struct spp_ble_gattc_char *next;
+} spp_ble_gattc_char_t;
+
+typedef struct {
+    int32_t len;
+    spp_ble_gattc_char_t * pfirst;
+    spp_ble_gattc_char_t * plast;
+} spp_ble_gattc_char_head;
+
+typedef struct at_ble_gattc_srv {
+    int index;
+    uint16_t conn_id;
+    esp_gatt_srvc_id_t srvc_id;
+    spp_ble_gattc_char_head * gattc_srv_char_head;
+    struct at_ble_gattc_srv *next;
+} spp_ble_gattc_srv_t;
+
+typedef struct {
+    int32_t len;
+    spp_ble_gattc_srv_t * pfirst;
+    spp_ble_gattc_srv_t * plast;
+} spp_ble_gattc_srv_head;
+
+static spp_ble_gattc_srv_head pAtGattcSrvHead = {
+    .len    = 0,
+    .pfirst = NULL,
+    .plast  = NULL
+};
+
+struct gattc_profile_inst {
+    esp_gattc_cb_t gattc_cb;
+    uint16_t gattc_if;
+    uint16_t app_id;
+    uint16_t conn_id;
+    esp_bd_addr_t remote_bda;
+};
+
+///Declare static functions
+static void esp_gap_cb(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param);
+static void esp_gattc_cb(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param);
+static void gattc_profile_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param);
+
+/* One gatt-based profile one app_id and one gattc_if, this array will store the gattc_if returned by ESP_GATTS_REG_EVT */
+static struct gattc_profile_inst gl_profile_tab[PROFILE_NUM] = {
+    [PROFILE_APP_ID] = {
+        .gattc_cb = gattc_profile_event_handler,
+        .gattc_if = ESP_GATT_IF_NONE,       /* Not get the gatt_if, so initial is ESP_GATT_IF_NONE */
+    },
+};
+
+static esp_ble_scan_params_t ble_scan_params = {
+    .scan_type              = BLE_SCAN_TYPE_ACTIVE,
+    .own_addr_type          = BLE_ADDR_TYPE_PUBLIC,
+    .scan_filter_policy     = BLE_SCAN_FILTER_ALLOW_ALL,
+    .scan_interval          = 0x50,
+    .scan_window            = 0x30
+};
+
+
+static const char device_name[] = "ESP_SPP_SERVER";
+static bool is_connect = false;
+static uint16_t spp_conn_id = 0;
+static uint16_t spp_mtu_size = 23;
+static uint16_t cmd = 0;
+static uint16_t wr_descr_ccc_num = 0;
+static uint16_t spp_gattc_if = 0xff;
+static int need_notify_number  = 0;
+static int char_descr_index_count = 0;
+static char * notify_value_p = NULL;
+static int notify_value_offset = 0;
+static spp_ble_gattc_srv_t * temp_srv_p = NULL;
+static spp_ble_gattc_char_t * notify_char_pp = NULL;
+static spp_ble_gattc_char_t notify_char_p[3] ;
+static spp_ble_gattc_char_t * temp_gattc_char_p1 = NULL;
+static spp_ble_gattc_char_descr_t * notify_char_descr_p[2] = {NULL,NULL};
+static spp_ble_gattc_char_descr_t * temp_char_descr_p = NULL;
+static spp_ble_gattc_char_head * pGattcCharHead = NULL;
+static esp_ble_gap_cb_param_t scan_rst;
+static xQueueHandle cmd_reg_queue = NULL;
+static xQueueHandle cmd_read_queue = NULL;
+QueueHandle_t spp_uart_queue = NULL;
+#ifdef SUPPORT_HEARTBEAT
+static uint8_t  heartbeat_s[9] = {'E','s','p','r','e','s','s','i','f'};
+static xQueueHandle cmd_heartbeat_queue = NULL;
+#endif
+
+static void print_srv_node(void)
+{
+    temp_srv_p = pAtGattcSrvHead.pfirst;
+
+    if(temp_srv_p == NULL){
+        ESP_LOGE(GATTC_TAG, "temp_srv_p == NULL,%s L#%d\n",__func__,__LINE__);
+    }
+    temp_srv_p = pAtGattcSrvHead.pfirst;
+    while(temp_srv_p != NULL){
+        ESP_LOGI(GATTC_TAG,"+SRV:No.%d,UUID:0x%04x\n", temp_srv_p->index,temp_srv_p->srvc_id.id.uuid.uuid.uuid16);
+        temp_srv_p = temp_srv_p->next;
+    }
+}
+
+static void get_char_and_descr(void)
+{
+    pGattcCharHead = (spp_ble_gattc_char_head *)malloc(sizeof(spp_ble_gattc_char_head));
+
+    pGattcCharHead->len = 0;
+    pGattcCharHead->pfirst = NULL;
+    pGattcCharHead->plast = NULL;
+    pAtGattcSrvHead.pfirst->gattc_srv_char_head = pGattcCharHead;
+    temp_srv_p = pAtGattcSrvHead.pfirst;
+
+    esp_ble_gattc_get_characteristic(spp_gattc_if, temp_srv_p->conn_id, &temp_srv_p->srvc_id, NULL);
+}
+
+static void Characteristic_Rd(int32_t srv_index,int32_t char_index)
+{
+    spp_ble_gattc_char_t * pGattc_char_node = NULL;
+
+    temp_srv_p = pAtGattcSrvHead.pfirst;
+    if(temp_srv_p == NULL) {
+        ESP_LOGE(GATTC_TAG, "temp_srv_p == NULL,%s L#%d\n",__func__,__LINE__);
+        return ;
+    }
+    while(--srv_index) {
+        temp_srv_p = temp_srv_p->next;
+        if (temp_srv_p == NULL) {
+            ESP_LOGE(GATTC_TAG, "temp_srv_p == NULL,%s L#%d\n",__func__,__LINE__);
+            return ;
+        }
+    }
+    pGattcCharHead = temp_srv_p->gattc_srv_char_head;
+    if(pGattcCharHead == NULL){
+        ESP_LOGE(GATTC_TAG, "pGattcCharHead == NULL,%s L#%d\n",__func__,__LINE__);
+        return;
+    }
+
+    pGattc_char_node = pGattcCharHead->pfirst;
+    if(pGattc_char_node == NULL) {
+        ESP_LOGE(GATTC_TAG, "pGattc_char_node == NULL,%s L#%d\n",__func__,__LINE__);
+        return ;
+    }
+    while(--char_index){
+        pGattc_char_node = pGattc_char_node->next;
+        if(pGattc_char_node == NULL){
+            ESP_LOGE(GATTC_TAG, "pGattc_char_node == NULL,%s L#%d\n",__func__,__LINE__);
+            return ;
+        }
+    }
+
+    esp_ble_gattc_read_char(spp_gattc_if, pGattc_char_node->conn_id, &pGattc_char_node->srvc_id, &pGattc_char_node->char_id, ESP_GATT_AUTH_REQ_NONE);
+}
+
+static void  Characteristic_Wr(int32_t srv_index,int32_t char_index,char * s ,size_t length)
+{
+    int32_t len = 0;
+    spp_ble_gattc_char_t * pGattc_char_node;
+    uint8_t * spp_gattc_wr_buffer = NULL;
+
+    if(length == 0) {
+        return ;
+    }
+    len = length;
+
+    temp_srv_p = pAtGattcSrvHead.pfirst;
+    if(temp_srv_p == NULL) {
+        ESP_LOGE(GATTC_TAG, "temp_srv_p == NULL,%s L#%d\n",__func__,__LINE__);
+        return ;
+    }
+    while(--srv_index) {
+        temp_srv_p = temp_srv_p->next;
+        if (temp_srv_p == NULL) {
+            ESP_LOGE(GATTC_TAG, "temp_srv_p == NULL,%s L#%d\n",__func__,__LINE__);
+            return ;
+        }
+    }
+
+    pGattcCharHead = temp_srv_p->gattc_srv_char_head;
+    if(pGattcCharHead == NULL){
+        ESP_LOGE(GATTC_TAG, "pGattcCharHead == NULL,%s L#%d\n",__func__,__LINE__);
+        return ;
+    }
+    pGattc_char_node = pGattcCharHead->pfirst;
+    if (pGattc_char_node == NULL) {
+        ESP_LOGE(GATTC_TAG, "pGattc_char_node == NULL,%s L#%d\n",__func__,__LINE__);
+        return ;
+    }
+    while(--char_index){
+        pGattc_char_node = pGattc_char_node->next;
+        if(pGattc_char_node == NULL){
+            ESP_LOGE(GATTC_TAG, "pGattc_char_node == NULL,%s L#%d\n",__func__,__LINE__);
+            return ;
+        }
+    }
+
+    spp_gattc_wr_buffer = (uint8_t *)malloc(sizeof(uint8_t)*(len));
+    memcpy(spp_gattc_wr_buffer,s,len);
+
+    if((pGattc_char_node->char_prop & (ESP_GATT_CHAR_PROP_BIT_WRITE_NR|ESP_GATT_CHAR_PROP_BIT_WRITE)) == 0){
+        ESP_LOGE(GATTC_TAG,"char_prop do not allow write\n");
+        free(spp_gattc_wr_buffer);
+        return ;
+    }
+
+    esp_ble_gattc_write_char(spp_gattc_if, pGattc_char_node->conn_id, &pGattc_char_node->srvc_id, &pGattc_char_node->char_id, len,spp_gattc_wr_buffer, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE);
+    free(spp_gattc_wr_buffer);
+
+    return ;
+}
+
+static void store_srv_info(esp_ble_gattc_cb_param_t * p_data)
+{
+    spp_ble_gattc_srv_t * pGattc_srv_node = NULL;
+
+    pGattc_srv_node = (spp_ble_gattc_srv_t *)malloc(sizeof(spp_ble_gattc_srv_t));
+    if (pGattc_srv_node == NULL) {
+        ESP_LOGE(GATTC_TAG, "%s at_malloc error:", __func__);
+        return;
+    }
+    pGattc_srv_node->conn_id = p_data->search_res.conn_id;
+    pGattc_srv_node->next = NULL;
+    pGattc_srv_node->gattc_srv_char_head = NULL;
+    pGattc_srv_node->index = pAtGattcSrvHead.len + 1;
+    pGattc_srv_node->srvc_id.is_primary = p_data->search_res.srvc_id.is_primary;
+    pGattc_srv_node->srvc_id.id.inst_id = p_data->search_res.srvc_id.id.inst_id;
+    pGattc_srv_node->srvc_id.id.uuid.len = p_data->search_res.srvc_id.id.uuid.len;
+    pGattc_srv_node->srvc_id.id.uuid.uuid.uuid16 = p_data->search_res.srvc_id.id.uuid.uuid.uuid16;
+
+    if (pAtGattcSrvHead.len == 0) {
+        pAtGattcSrvHead.len++;
+        pAtGattcSrvHead.pfirst = pGattc_srv_node;
+        pAtGattcSrvHead.plast = pGattc_srv_node;
+    } else {
+        pAtGattcSrvHead.len++;
+        pAtGattcSrvHead.plast->next = pGattc_srv_node;
+        pAtGattcSrvHead.plast = pGattc_srv_node;
+    }
+}
+
+static void store_char_info(esp_ble_gattc_cb_param_t * p_data)
+{
+    spp_ble_gattc_char_t * pGattc_char_node = NULL;
+
+    pGattc_char_node = (spp_ble_gattc_char_t *)malloc(sizeof(spp_ble_gattc_char_t));
+    pGattc_char_node->srv_index = temp_srv_p->index;
+    pGattc_char_node->char_descr = NULL;
+    pGattc_char_node->conn_id = p_data->get_char.conn_id;
+    pGattc_char_node->next = NULL;
+    pGattc_char_node->index = pGattcCharHead->len + 1;
+    pGattc_char_node->srvc_id.is_primary = p_data->get_char.srvc_id.is_primary;
+    pGattc_char_node->srvc_id.id.inst_id = p_data->get_char.srvc_id.id.inst_id;
+    pGattc_char_node->srvc_id.id.uuid.len = p_data->get_char.srvc_id.id.uuid.len;
+    pGattc_char_node->srvc_id.id.uuid.uuid.uuid16 = p_data->get_char.srvc_id.id.uuid.uuid.uuid16;
+
+    pGattc_char_node->char_id.inst_id = p_data->get_char.char_id.inst_id;
+    pGattc_char_node->char_id.uuid.len = p_data->get_char.char_id.uuid.len;
+    pGattc_char_node->char_prop = p_data->get_char.char_prop;
+    pGattc_char_node->char_id.uuid.uuid.uuid16 = p_data->get_char.char_id.uuid.uuid.uuid16;
+
+    temp_gattc_char_p1 = pGattc_char_node;
+
+    if (pGattcCharHead->len == 0) {
+        pGattcCharHead->len++;
+        pGattcCharHead->pfirst = pGattc_char_node;
+        pGattcCharHead->plast = pGattc_char_node;
+    } else {
+        pGattcCharHead->len++;
+        pGattcCharHead->plast->next = pGattc_char_node;
+        pGattcCharHead->plast = pGattc_char_node;
+    }
+    esp_ble_gattc_get_descriptor(spp_gattc_if, pGattc_char_node->conn_id, &pGattc_char_node->srvc_id, &pGattc_char_node->char_id, NULL);
+}
+
+static void store_desc_info(esp_ble_gattc_cb_param_t * p_data)
+{
+    spp_ble_gattc_char_descr_t * pGattc_char_node_descr = NULL;
+
+    temp_char_descr_p = temp_gattc_char_p1->char_descr;
+    pGattc_char_node_descr = (spp_ble_gattc_char_descr_t *)malloc(sizeof(spp_ble_gattc_char_descr_t));
+    pGattc_char_node_descr->conn_id = p_data->get_descr.conn_id;
+    pGattc_char_node_descr->char_descr_index = ++char_descr_index_count;
+    if (temp_char_descr_p == NULL)
+        temp_gattc_char_p1->char_descr = pGattc_char_node_descr;
+    else {
+        while (temp_char_descr_p->next != NULL) {
+            temp_char_descr_p = temp_char_descr_p->next;
+        }
+        temp_char_descr_p->next = pGattc_char_node_descr;
+    }
+    pGattc_char_node_descr->next = NULL;
+    pGattc_char_node_descr->descr_id.inst_id = p_data->get_descr.descr_id.inst_id;
+    pGattc_char_node_descr->descr_id.uuid.len = p_data->get_descr.descr_id.uuid.len;
+
+    pGattc_char_node_descr->descr_id.uuid.uuid.uuid16 = p_data->get_descr.descr_id.uuid.uuid.uuid16;
+    if((p_data->get_descr.descr_id.uuid.uuid.uuid16 == 0x2902)&&(need_notify_number<3)){
+        memset(&(notify_char_p[need_notify_number]),0x0,sizeof(spp_ble_gattc_char_t));
+        memcpy(&(notify_char_p[need_notify_number]),temp_gattc_char_p1,sizeof(spp_ble_gattc_char_t));
+        notify_char_descr_p[need_notify_number] = pGattc_char_node_descr;
+        ESP_LOGI(GATTC_TAG,"need_notify_number=%d\n",need_notify_number);
+        ESP_LOGI(GATTC_TAG,"- srvc_id = 0x%04x, char_id = 0x%04x \n", notify_char_p[need_notify_number].srvc_id.id.uuid.uuid.uuid16, notify_char_p[need_notify_number].char_id.uuid.uuid.uuid16);
+        need_notify_number++;
+    }
+    esp_ble_gattc_get_descriptor(spp_gattc_if, temp_gattc_char_p1->conn_id, &temp_gattc_char_p1->srvc_id, &temp_gattc_char_p1->char_id, &pGattc_char_node_descr->descr_id);
+}
+
+static void notify_event_handler(esp_ble_gattc_cb_param_t * p_data)
+{
+    uint8_t gattc_srv_index_for_port = 0;
+    uint8_t gattc_char_index_for_port = 0;
+
+    temp_srv_p = pAtGattcSrvHead.pfirst;
+    while(temp_srv_p != NULL){
+        if(temp_srv_p->srvc_id.id.uuid.uuid.uuid16 ==  p_data->notify.srvc_id.id.uuid.uuid.uuid16){
+            gattc_srv_index_for_port = temp_srv_p->index;
+            pGattcCharHead = temp_srv_p->gattc_srv_char_head;
+            if(pGattcCharHead != NULL){
+                notify_char_pp = pGattcCharHead->pfirst;
+                while(notify_char_pp != NULL){
+                    if(notify_char_pp->char_id.uuid.uuid.uuid16== p_data->notify.char_id.uuid.uuid.uuid16){
+                        gattc_char_index_for_port = notify_char_pp->index;
+                    }
+                    notify_char_pp = notify_char_pp->next;
+                }
+            }
+        }
+        temp_srv_p = temp_srv_p->next;
+    }
+    if(p_data->notify.is_notify == true){
+        ESP_LOGI(GATTC_TAG,"+NOTIFY:%d,%d,%d,",gattc_srv_index_for_port,gattc_char_index_for_port,p_data->notify.value_len);
+    }else{
+        ESP_LOGI(GATTC_TAG,"+INDICATE:%d,%d,%d,",gattc_srv_index_for_port,gattc_char_index_for_port,p_data->notify.value_len);
+    }
+    if((gattc_srv_index_for_port == SPP_SRV_INDEX)&&(gattc_char_index_for_port == SPP_DATA_NTFY_CHAR_INDEX)){
+#ifdef SPP_DEBUG_MODE
+        esp_log_buffer_char(GATTC_TAG, (char *)p_data->notify.value, p_data->notify.value_len);
+#else
+        if((p_data->notify.value[0] == '#')&&(p_data->notify.value[1] == '#')){
+            if(p_data->notify.value[3] == 1){
+                notify_value_p = (char *)malloc(120*sizeof(char));
+                if(notify_value_p == NULL){
+                    ESP_LOGE(GATTC_TAG,"malloc failed,%s\n",__func__);
+                    return;
+                }
+                memcpy(notify_value_p,p_data->notify.value,p_data->notify.value_len);
+                if(p_data->notify.value[2] == p_data->notify.value[3]){
+                    uart_write_bytes(UART_NUM_0, (char *)(notify_value_p), (p_data->notify.value_len + notify_value_offset));
+                    free(notify_value_p);
+                    notify_value_p = NULL;
+                    notify_value_offset = 0;
+                    return;
+                }
+                notify_value_offset += p_data->notify.value_len;
+            }else if(p_data->notify.value[3] <= p_data->notify.value[2]){
+                memcpy((notify_value_p + notify_value_offset),p_data->notify.value,p_data->notify.value_len);
+                if(p_data->notify.value[2] == p_data->notify.value[3]){
+                    uart_write_bytes(UART_NUM_0, (char *)(notify_value_p), (p_data->notify.value_len + notify_value_offset));
+                    free(notify_value_p);
+                    notify_value_p = NULL;
+                    notify_value_offset = 0;
+                    return;
+                }
+                notify_value_offset += p_data->notify.value_len;
+            }
+        }else{
+            uart_write_bytes(UART_NUM_0, (char *)(p_data->notify.value), p_data->notify.value_len);
+        }
+#endif
+    }else if((gattc_srv_index_for_port == SPP_SRV_INDEX)&&(gattc_char_index_for_port == SPP_STATUS_CHAR_INDEX)){
+        esp_log_buffer_char(GATTC_TAG, (char *)p_data->notify.value, p_data->notify.value_len);
+        //TODO:server notify status characteristic
+    }else{
+        esp_log_buffer_char(GATTC_TAG, (char *)p_data->notify.value, p_data->notify.value_len);
+    }
+}
+
+static void read_event_handler(esp_ble_gattc_cb_param_t * p_data)
+{
+    uint8_t gattc_srv_index_for_port = 0;
+    uint8_t gattc_char_index_for_port = 0;
+
+    temp_srv_p = pAtGattcSrvHead.pfirst;
+    while(temp_srv_p != NULL){
+        if(temp_srv_p->srvc_id.id.uuid.uuid.uuid16 ==  p_data->notify.srvc_id.id.uuid.uuid.uuid16){
+            gattc_srv_index_for_port = temp_srv_p->index;
+            pGattcCharHead = temp_srv_p->gattc_srv_char_head;
+            if(pGattcCharHead != NULL){
+                notify_char_pp = pGattcCharHead->pfirst;
+                while(notify_char_pp != NULL){
+                    if(notify_char_pp->char_id.uuid.uuid.uuid16== p_data->notify.char_id.uuid.uuid.uuid16){
+                        gattc_char_index_for_port = notify_char_pp->index;
+                    }
+                    notify_char_pp = notify_char_pp->next;
+                }
+            }
+        }
+        temp_srv_p = temp_srv_p->next;
+    }
+
+    if((gattc_srv_index_for_port == SPP_SRV_INDEX)&&(gattc_char_index_for_port == SPP_STATUS_CHAR_INDEX)){
+        //TODO: read status characteristic
+    }
+}
+
+static void printf_char_and_descr(uint8_t srv_index)
+{
+    spp_ble_gattc_char_t * temp_gattc_char_p = NULL;
+    spp_ble_gattc_char_descr_t * temp_gattc_char_descr_p = NULL;
+
+    temp_srv_p = pAtGattcSrvHead.pfirst;
+
+    if(temp_srv_p == NULL){
+        ESP_LOGE(GATTC_TAG, "srv = NULL\n");
+    }
+    while(--srv_index){
+        temp_srv_p = temp_srv_p->next;
+        if(temp_srv_p == NULL){
+            ESP_LOGE(GATTC_TAG, "srv = NULL\n");
+        }
+    }
+
+    temp_gattc_char_p = temp_srv_p->gattc_srv_char_head->pfirst;
+    while(temp_gattc_char_p != NULL){
+        ESP_LOGI(GATTC_TAG,"+CHAR:%d,%d,UUID:0x%04x",temp_srv_p->index, temp_gattc_char_p->index,temp_gattc_char_p->char_id.uuid.uuid.uuid16);
+
+        temp_gattc_char_descr_p = temp_gattc_char_p->char_descr;
+        while(temp_gattc_char_descr_p != NULL){
+            ESP_LOGI(GATTC_TAG,"+DESC:%d,%d,%d,UUID:0x%04x",temp_srv_p->index, temp_gattc_char_p->index,temp_gattc_char_descr_p->char_descr_index,temp_gattc_char_descr_p->descr_id.uuid.uuid.uuid16);
+            temp_gattc_char_descr_p = temp_gattc_char_descr_p->next;
+        }
+        temp_gattc_char_p = temp_gattc_char_p->next;
+    }
+    return ;
+}
+
+static void esp_gap_cb(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param)
+{
+    uint8_t *adv_name = NULL;
+    uint8_t adv_name_len = 0;
+
+    switch(event){
+    case ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT: {
+        if(param->scan_param_cmpl.status != ESP_BT_STATUS_SUCCESS){
+            ESP_LOGE(GATTC_TAG, "Scan param set failed");
+            break;
+        }
+        //the unit of the duration is second
+        uint32_t duration = 0xFFFF;
+        ESP_LOGE(GATTC_TAG, "Enable Ble Scan:during time 0x%04X minutes.",duration);
+        esp_ble_gap_start_scanning(duration);
+        break;
+    }
+    case ESP_GAP_BLE_SCAN_START_COMPLETE_EVT:
+        //scan start complete event to indicate scan start successfully or failed
+        if (param->scan_start_cmpl.status != ESP_BT_STATUS_SUCCESS) {
+            ESP_LOGE(GATTC_TAG, "Scan start failed");
+            break;
+        }
+        ESP_LOGI(GATTC_TAG, "Scan start successed");
+        break;
+    case ESP_GAP_BLE_SCAN_STOP_COMPLETE_EVT:
+        if (param->scan_stop_cmpl.status != ESP_BT_STATUS_SUCCESS) {
+            ESP_LOGE(GATTC_TAG, "Scan stop failed");
+            break;
+        }
+        ESP_LOGI(GATTC_TAG, "Scan stop successed");
+        if (is_connect == false) {
+            ESP_LOGI(GATTC_TAG, "Connect to the remote device.");
+            esp_ble_gattc_open(gl_profile_tab[PROFILE_APP_ID].gattc_if, scan_rst.scan_rst.bda, true);
+        }
+        break;
+    case ESP_GAP_BLE_SCAN_RESULT_EVT: {
+        esp_ble_gap_cb_param_t *scan_result = (esp_ble_gap_cb_param_t *)param;
+        switch (scan_result->scan_rst.search_evt) {
+        case ESP_GAP_SEARCH_INQ_RES_EVT:
+            esp_log_buffer_hex(GATTC_TAG, scan_result->scan_rst.bda, 6);
+            ESP_LOGI(GATTC_TAG, "Searched Adv Data Len %d, Scan Response Len %d", scan_result->scan_rst.adv_data_len, scan_result->scan_rst.scan_rsp_len);
+            adv_name = esp_ble_resolve_adv_data(scan_result->scan_rst.ble_adv,ESP_BLE_AD_TYPE_NAME_CMPL, &adv_name_len);
+            ESP_LOGI(GATTC_TAG, "Searched Device Name Len %d", adv_name_len);
+            esp_log_buffer_char(GATTC_TAG, adv_name, adv_name_len);
+            ESP_LOGI(GATTC_TAG, "\n");
+            if (adv_name != NULL) {
+                if ( strncmp((char *)adv_name, device_name, adv_name_len) == 0) {
+                    memcpy(&(scan_rst), scan_result, sizeof(esp_ble_gap_cb_param_t));
+                    esp_ble_gap_stop_scanning();
+                }
+            }
+            break;
+        case ESP_GAP_SEARCH_INQ_CMPL_EVT:
+            break;
+        default:
+            break;
+        }
+        break;
+    }
+    case ESP_GAP_BLE_ADV_STOP_COMPLETE_EVT:
+        if (param->adv_stop_cmpl.status != ESP_BT_STATUS_SUCCESS){
+            ESP_LOGE(GATTC_TAG, "Adv stop failed");
+        }else {
+            ESP_LOGI(GATTC_TAG, "Stop adv successfully");
+        }
+        break;
+    default:
+        break;
+    }
+}
+
+static void esp_gattc_cb(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param)
+{
+    ESP_LOGI(GATTC_TAG, "EVT %d, gattc if %d", event, gattc_if);
+
+    /* If event is register event, store the gattc_if for each profile */
+    if (event == ESP_GATTC_REG_EVT) {
+        if (param->reg.status == ESP_GATT_OK) {
+            gl_profile_tab[param->reg.app_id].gattc_if = gattc_if;
+        } else {
+            ESP_LOGI(GATTC_TAG, "Reg app failed, app_id %04x, status %d",param->reg.app_id,param->reg.status);
+            return;
+        }
+    }
+    /* If the gattc_if equal to profile A, call profile A cb handler,
+     * so here call each profile's callback */
+    do {
+        int idx;
+        for (idx = 0; idx < PROFILE_NUM; idx++) {
+            if (gattc_if == ESP_GATT_IF_NONE || /* ESP_GATT_IF_NONE, not specify a certain gatt_if, need to call every profile cb function */
+                    gattc_if == gl_profile_tab[idx].gattc_if) {
+                if (gl_profile_tab[idx].gattc_cb) {
+                    gl_profile_tab[idx].gattc_cb(event, gattc_if, param);
+                }
+            }
+        }
+    } while (0);
+}
+
+static void gattc_profile_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param)
+{
+    esp_ble_gattc_cb_param_t *p_data = (esp_ble_gattc_cb_param_t *)param;
+
+    switch (event) {
+    case ESP_GATTC_REG_EVT:
+        ESP_LOGE(GATTC_TAG, "REG EVT, set scan params");
+        esp_ble_gap_set_scan_params(&ble_scan_params);
+        break;
+    case ESP_GATTC_CONNECT_EVT:
+        if(p_data->connect.status != ESP_GATT_OK){
+            ESP_LOGI(GATTC_TAG, "connect fail, status = %d", p_data->connect.status);
+            break;
+        }
+        spp_gattc_if = gattc_if;
+        is_connect = true;
+        spp_conn_id = p_data->connect.conn_id;
+        memcpy(gl_profile_tab[PROFILE_APP_ID].remote_bda, p_data->connect.remote_bda, sizeof(esp_bd_addr_t));
+        ESP_LOGI(GATTC_TAG, "ESP_GATTC_CONNECT_EVT: conn_id=%d, gatt_if = %d, status =%d", spp_conn_id, gattc_if, p_data->connect.status);
+        ESP_LOGI(GATTC_TAG, "REMOTE BDA:");
+        esp_log_buffer_hex(GATTC_TAG, gl_profile_tab[PROFILE_APP_ID].remote_bda, sizeof(esp_bd_addr_t));
+        esp_ble_gattc_search_service(gattc_if, spp_conn_id, NULL);
+        break;
+    case ESP_GATTC_DISCONNECT_EVT:
+        ESP_LOGI(GATTC_TAG, "disconnect , status = %d", p_data->disconnect.status);
+        is_connect = false;
+        break;
+    case ESP_GATTC_SEARCH_RES_EVT:
+        if((p_data->search_res.srvc_id.id.uuid.len == ESP_UUID_LEN_16)&&(p_data->search_res.srvc_id.id.uuid.uuid.uuid16 == 0x1800)){
+            ESP_LOGI(GATTC_TAG, "uuid == 0x1800\n");
+            break;
+        }
+        if((p_data->search_res.srvc_id.id.uuid.len == ESP_UUID_LEN_16)&&(p_data->search_res.srvc_id.id.uuid.uuid.uuid16 == 0x1801)){
+            ESP_LOGI(GATTC_TAG, "uuid == 0x1801\n");
+            break;
+        }
+        store_srv_info(p_data);
+        break;
+    case ESP_GATTC_SEARCH_CMPL_EVT:
+        ESP_LOGI(GATTC_TAG, "SEARCH_CMPL: conn_id = %x, status %d", spp_conn_id, p_data->search_cmpl.status);
+        esp_ble_gattc_config_mtu(gattc_if,spp_conn_id, 512);
+        break;
+    case ESP_GATTC_GET_CHAR_EVT:
+        if (p_data->get_char.status != ESP_GATT_OK) {
+            printf_char_and_descr(SPP_SRV_INDEX);
+            xQueueSend(cmd_reg_queue,&cmd,10/portTICK_PERIOD_MS);
+            cmd++;
+            break;
+        }
+        store_char_info(p_data);
+        break;
+    case ESP_GATTC_GET_DESCR_EVT:
+        if (p_data->get_descr.status != ESP_GATT_OK) {
+            char_descr_index_count = 0;
+            esp_ble_gattc_get_characteristic(spp_gattc_if, temp_srv_p->conn_id, &temp_srv_p->srvc_id, &temp_gattc_char_p1->char_id);
+            break;
+        }
+        store_desc_info(p_data);
+        break;
+    case ESP_GATTC_REG_FOR_NOTIFY_EVT: {
+        ESP_LOGI(GATTC_TAG,"\nwr_descr_cmd = %d-- srvc_id = 0x%04x, char_id = 0x%04x, descr_id = 0x%04x \n",wr_descr_ccc_num , notify_char_p[wr_descr_ccc_num].srvc_id.id.uuid.uuid.uuid16, notify_char_p[wr_descr_ccc_num].char_id.uuid.uuid.uuid16,notify_char_descr_p[wr_descr_ccc_num]->descr_id.uuid.uuid.uuid16);
+
+        uint16_t notify_en = 1;
+        esp_ble_gattc_write_char_descr(
+                spp_gattc_if,
+                notify_char_p[wr_descr_ccc_num].conn_id,
+                &(notify_char_p[wr_descr_ccc_num].srvc_id),
+                &(notify_char_p[wr_descr_ccc_num].char_id),
+                &notify_char_descr_p[wr_descr_ccc_num]->descr_id,
+                sizeof(notify_en),
+                (uint8_t *)&notify_en,
+                ESP_GATT_WRITE_TYPE_RSP,
+                ESP_GATT_AUTH_REQ_NONE);
+
+        wr_descr_ccc_num++;
+        break;
+    }
+    case ESP_GATTC_NOTIFY_EVT:
+        ESP_LOGI(GATTC_TAG,"ESP_GATTC_NOTIFY_EVT\n");
+        notify_event_handler(p_data);
+        break;
+    case ESP_GATTC_READ_CHAR_EVT:
+        ESP_LOGI(GATTC_TAG,"ESP_GATTC_READ_CHAR_EVT\n");
+        read_event_handler(p_data);
+        break;
+    case ESP_GATTC_WRITE_CHAR_EVT:
+        ESP_LOGI(GATTC_TAG,"ESP_GATTC_WRITE_CHAR_EVT:status=%d,srvc_uuid = 0x%04x, char_uuid = 0x%04x, descr_uuid=0x%04x \n",p_data->write.status , p_data->write.srvc_id.id.uuid.uuid.uuid16, p_data->write.char_id.uuid.uuid.uuid16, p_data->write.descr_id.uuid.uuid.uuid16);
+        break;
+    case ESP_GATTC_PREP_WRITE_EVT:
+        break;
+    case ESP_GATTC_WRITE_DESCR_EVT:
+        ESP_LOGI(GATTC_TAG,"ESP_GATTC_WRITE_DESCR_EVT: status =%d,srvc_uuid = 0x%04x, char_uuid = 0x%04x, descr_uuid=0x%04x \n",p_data->write.status,p_data->write.srvc_id.id.uuid.uuid.uuid16, p_data->write.char_id.uuid.uuid.uuid16, p_data->write.descr_id.uuid.uuid.uuid16);
+        xQueueSend(cmd_reg_queue,&cmd,10/portTICK_PERIOD_MS);
+        cmd++;
+        break;
+    case ESP_GATTC_CFG_MTU_EVT:
+        if(p_data->cfg_mtu.status != ESP_OK){
+            break;
+        }
+        ESP_LOGI(GATTC_TAG,"+MTU:%d\n", p_data->cfg_mtu.mtu);
+        spp_mtu_size = p_data->cfg_mtu.mtu;
+        print_srv_node();
+        get_char_and_descr();
+        break;
+    case ESP_GATTC_SRVC_CHG_EVT:
+        break;
+    default:
+        break;
+    }
+}
+
+void spp_client_reg_task(void* arg)
+{
+    uint16_t cmd_id;
+    for(;;) {
+        vTaskDelay(100 / portTICK_PERIOD_MS);
+        if(xQueueReceive(cmd_reg_queue, &cmd_id, portMAX_DELAY)) {
+            if(0 == cmd_id){
+                esp_ble_gattc_register_for_notify(spp_gattc_if, gl_profile_tab[PROFILE_APP_ID].remote_bda ,&(notify_char_p[cmd_id].srvc_id), &(notify_char_p[cmd_id].char_id));
+                ESP_LOGI(GATTC_TAG,"No.%d-- srvc_id = 0x%04x, char_id = 0x%04x \n",cmd_id, notify_char_p[cmd_id].srvc_id.id.uuid.uuid.uuid16, notify_char_p[cmd_id].char_id.uuid.uuid.uuid16);
+            }else if(1 == cmd_id){
+                esp_ble_gattc_register_for_notify(spp_gattc_if, gl_profile_tab[PROFILE_APP_ID].remote_bda ,&(notify_char_p[cmd_id].srvc_id), &(notify_char_p[cmd_id].char_id));
+                ESP_LOGI(GATTC_TAG,"No.%d-- srvc_id = 0x%04x, char_id = 0x%04x \n",cmd_id, notify_char_p[cmd_id].srvc_id.id.uuid.uuid.uuid16, notify_char_p[cmd_id].char_id.uuid.uuid.uuid16);
+            }else if(2 == cmd_id){
+                esp_ble_gattc_register_for_notify(spp_gattc_if, gl_profile_tab[PROFILE_APP_ID].remote_bda ,&(notify_char_p[cmd_id].srvc_id), &(notify_char_p[cmd_id].char_id));
+                ESP_LOGI(GATTC_TAG,"No.%d-- srvc_id = 0x%04x, char_id = 0x%04x \n",cmd_id, notify_char_p[cmd_id].srvc_id.id.uuid.uuid.uuid16, notify_char_p[cmd_id].char_id.uuid.uuid.uuid16);
+            }
+#ifdef SUPPORT_HEARTBEAT
+            else if(3 == cmd_id){
+                xQueueSend(cmd_heartbeat_queue,&cmd,10/portTICK_PERIOD_MS);
+            }
+#endif
+        }
+    }
+}
+
+#ifdef SUPPORT_HEARTBEAT
+void spp_heart_beat_task(void * arg)
+{
+    uint16_t cmd_id;
+
+    for(;;) {
+        vTaskDelay(50 / portTICK_PERIOD_MS);
+        if(xQueueReceive(cmd_heartbeat_queue, &cmd_id, portMAX_DELAY)) {
+            while(1){
+                if(is_connect == true){
+                    Characteristic_Wr(SPP_SRV_INDEX,SPP_HEARTBEAT_CHAR_INDEX,(char *)heartbeat_s,sizeof(heartbeat_s));
+                    vTaskDelay(5000 / portTICK_PERIOD_MS);
+                }else{
+                    ESP_LOGI(GATTC_TAG,"disconnect\n");
+                    break;
+                }
+            }
+        }
+    }
+}
+#endif
+
+void spp_client_read_task(void * arg)
+{
+    uint16_t cmd_id;
+    int32_t srv_index = SPP_SRV_INDEX, char_index = SPP_STATUS_CHAR_INDEX;
+
+    for(;;) {
+        vTaskDelay(50 / portTICK_PERIOD_MS);
+        if(xQueueReceive(cmd_read_queue, &cmd_id, portMAX_DELAY)) {
+            if(is_connect == true){
+                Characteristic_Rd(srv_index,char_index);
+            }else{
+                ESP_LOGI(GATTC_TAG,"disconnect\n");
+            }
+        }
+    }
+}
+
+void ble_client_appRegister(void)
+{
+    esp_err_t status;
+
+    ESP_LOGI(GATTC_TAG, "register callback");
+
+    //register the scan callback function to the gap module
+    if ((status = esp_ble_gap_register_callback(esp_gap_cb)) != ESP_OK) {
+        ESP_LOGE(GATTC_TAG, "gap register error, error code = %x", status);
+        return;
+    }
+    //register the callback function to the gattc module
+    if ((status = esp_ble_gattc_register_callback(esp_gattc_cb)) != ESP_OK) {
+        ESP_LOGE(GATTC_TAG, "gattc register error, error code = %x", status);
+        return;
+    }
+    esp_ble_gattc_app_register(PROFILE_APP_ID);
+
+    cmd_reg_queue = xQueueCreate(10, sizeof(uint32_t));
+    xTaskCreate(spp_client_reg_task, "spp_client_reg_task", 2048, NULL, 10, NULL);
+
+#ifdef SUPPORT_HEARTBEAT
+    cmd_heartbeat_queue = xQueueCreate(10, sizeof(uint32_t));
+    xTaskCreate(spp_heart_beat_task, "spp_heart_beat_task", 2048, NULL, 10, NULL);
+#endif
+
+    cmd_read_queue = xQueueCreate(10, sizeof(uint32_t));
+    xTaskCreate(spp_client_read_task, "spp_client_read_task", 2048, NULL, 10, NULL);
+}
+
+void uart_task(void *pvParameters)
+{
+    uart_event_t event;
+    for (;;) {
+        //Waiting for UART event.
+        if (xQueueReceive(spp_uart_queue, (void * )&event, (portTickType)portMAX_DELAY)) {
+            switch (event.type) {
+            //Event of UART receving data
+            case UART_DATA:
+                if (event.size) {
+                    uint8_t * temp = NULL;
+                    temp = (uint8_t *)malloc(sizeof(uint8_t)*event.size);
+                    memset(temp,0x0,event.size);
+                    uart_read_bytes(UART_NUM_0,temp,event.size,portMAX_DELAY);
+                    Characteristic_Wr(SPP_SRV_INDEX,SPP_DATA_RECV_CHAR_INDEX,(char *)temp,event.size);
+                    free(temp);
+                }
+                break;
+            default:
+                break;
+            }
+        }
+    }
+    vTaskDelete(NULL);
+}
+
+static void spp_uart_init(void)
+{
+    uart_config_t uart_config = {
+        .baud_rate = 115200,
+        .data_bits = UART_DATA_8_BITS,
+        .parity = UART_PARITY_DISABLE,
+        .stop_bits = UART_STOP_BITS_1,
+        .flow_ctrl = UART_HW_FLOWCTRL_RTS,
+        .rx_flow_ctrl_thresh = 122,
+    };
+
+    //Set UART parameters
+    uart_param_config(UART_NUM_0, &uart_config);
+    //Set UART pins
+    uart_set_pin(UART_NUM_0, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);
+    //Install UART driver, and get the queue.
+    uart_driver_install(UART_NUM_0, 4096, 8192, 10,&spp_uart_queue,0);
+    xTaskCreate(uart_task, "uTask", 2048, (void*)UART_NUM_0, 8, NULL);
+}
+
+void app_main()
+{
+    esp_err_t ret;
+    esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT();
+
+    nvs_flash_init();
+    ret = esp_bt_controller_init(&bt_cfg);
+    if (ret) {
+        ESP_LOGE(GATTC_TAG, "%s enable controller failed\n", __func__);
+        return;
+    }
+
+    ret = esp_bt_controller_enable(ESP_BT_MODE_BTDM);
+    if (ret) {
+        ESP_LOGE(GATTC_TAG, "%s enable controller failed\n", __func__);
+        return;
+    }
+
+    ESP_LOGI(GATTC_TAG, "%s init bluetooth\n", __func__);
+    ret = esp_bluedroid_init();
+    if (ret) {
+        ESP_LOGE(GATTC_TAG, "%s init bluetooth failed\n", __func__);
+        return;
+    }
+    ret = esp_bluedroid_enable();
+    if (ret) {
+        ESP_LOGE(GATTC_TAG, "%s enable bluetooth failed\n", __func__);
+        return;
+    }
+
+    ble_client_appRegister();
+    spp_uart_init();
+}
+
diff --git a/examples/bluetooth/ble_spp_client/sdkconfig.defaults b/examples/bluetooth/ble_spp_client/sdkconfig.defaults
new file mode 100644 (file)
index 0000000..9d51df5
--- /dev/null
@@ -0,0 +1,4 @@
+# Override some defaults so BT stack is enabled
+# and WiFi disabled by default in this example
+CONFIG_BT_ENABLED=y
+CONFIG_WIFI_ENABLED=n