From b3fa390681f57155fd33ddc220316f9e1f3f7f4e Mon Sep 17 00:00:00 2001 From: xiewenxiang Date: Wed, 23 Aug 2017 17:40:54 +0800 Subject: [PATCH] component/bt: add example for ble spp client - this demo needs to work with the spp server demo --- examples/bluetooth/ble_spp_client/Makefile | 10 + examples/bluetooth/ble_spp_client/README.rst | 3 + .../ble_spp_client/main/component.mk | 4 + .../ble_spp_client/main/spp_client_demo.c | 887 ++++++++++++++++++ .../ble_spp_client/sdkconfig.defaults | 4 + 5 files changed, 908 insertions(+) create mode 100644 examples/bluetooth/ble_spp_client/Makefile create mode 100644 examples/bluetooth/ble_spp_client/README.rst create mode 100644 examples/bluetooth/ble_spp_client/main/component.mk create mode 100644 examples/bluetooth/ble_spp_client/main/spp_client_demo.c create mode 100644 examples/bluetooth/ble_spp_client/sdkconfig.defaults diff --git a/examples/bluetooth/ble_spp_client/Makefile b/examples/bluetooth/ble_spp_client/Makefile new file mode 100644 index 000000000..9628803c6 --- /dev/null +++ b/examples/bluetooth/ble_spp_client/Makefile @@ -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 index 000000000..b0d24946c --- /dev/null +++ b/examples/bluetooth/ble_spp_client/README.rst @@ -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 index 000000000..a98f634ea --- /dev/null +++ b/examples/bluetooth/ble_spp_client/main/component.mk @@ -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 index 000000000..627abf63f --- /dev/null +++ b/examples/bluetooth/ble_spp_client/main/spp_client_demo.c @@ -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 +#include +#include +#include +#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), + ¬ify_char_descr_p[wr_descr_ccc_num]->descr_id, + sizeof(notify_en), + (uint8_t *)¬ify_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 index 000000000..9d51df5ee --- /dev/null +++ b/examples/bluetooth/ble_spp_client/sdkconfig.defaults @@ -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