Closed krupis closed 1 year ago
@krupis Are you using ESP32 or ESP32-S3? If it is esp32 chip, it does not support multi-adv, iBeacon adv is unconnectable, so iBeacon adv packet and connectable adv cannot be sent at the same time, only one of the two can be selected at the same time. If it is esp32-S3 chip, it can support multi-adv, iBeacon adv and connectable adv can be sent at the same time.
@krupis Are you using ESP32 or ESP32-S3? If it is esp32 chip, it does not support multi-adv, iBeacon adv is unconnectable, so iBeacon adv packet and connectable adv cannot be sent at the same time, only one of the two can be selected at the same time. If it is esp32-S3 chip, it can support multi-adv, iBeacon adv and connectable adv can be sent at the same time.
The example code that I am using was programmed into ESP32. However, I also have ESP32-S3 development board and I can try to flash the same code to the ESP32-S3. Are you suggesting that the same code will work on the ESP32-S3 or I need to use a different technique of creating and managing GAP profiles?
Is there any examples for multi advertisement (ibeacon and connectable available)
if you want to test multi-adv, you can test the example esp-idf/examples/bluetooth/bluedroid/ble_50/multi-adv
on ESP32-S3 @krupis
if you want to test multi-adv, you can test the example
esp-idf/examples/bluetooth/bluedroid/ble_50/multi-adv
on ESP32-S3 @krupis
Hello. I have flashed the multi-adv example and I have some questions.
I have changed the example code slightly for testing purposes:
/*
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
/****************************************************************************
*
* This demo showcases BLE GATT server. It can send adv data, be connected by client.
* Run the gatt_client demo, the client demo will automatically connect to the gatt_server demo.
* Client demo will enable gatt_server's notify after connection. The two devices will then exchange
* data.
*
****************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#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 "esp_bt.h"
#include "esp_gap_ble_api.h"
#include "esp_gatts_api.h"
#include "esp_bt_defs.h"
#include "esp_bt_main.h"
#include "esp_gatt_common_api.h"
#include "sdkconfig.h"
#include "freertos/semphr.h"
#define LOG_TAG "MULTI_ADV_DEMO"
#define FUNC_SEND_WAIT_SEM(func, sem) do {\
esp_err_t __err_rc = (func);\
if (__err_rc != ESP_OK) { \
ESP_LOGE(LOG_TAG, "%s, message send fail, error = %d", __func__, __err_rc); \
} \
xSemaphoreTake(sem, portMAX_DELAY); \
} while(0);
static SemaphoreHandle_t test_sem = NULL;
uint8_t addr_1m[6] = {0xc0, 0xde, 0x52, 0x00, 0x00, 0x01};
uint8_t addr_2m[6] = {0xc0, 0xde, 0x52, 0x00, 0x00, 0x02};
esp_ble_gap_ext_adv_params_t ext_adv_params_1M = {
.type = ESP_BLE_GAP_SET_EXT_ADV_PROP_CONNECTABLE,
.interval_min = 0x30,
.interval_max = 0x30,
.channel_map = ADV_CHNL_ALL,
.filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY,
.primary_phy = ESP_BLE_GAP_PHY_1M,
.max_skip = 0,
.secondary_phy = ESP_BLE_GAP_PHY_1M,
.sid = 0,
.scan_req_notif = false,
.own_addr_type = BLE_ADDR_TYPE_RANDOM,
};
esp_ble_gap_ext_adv_params_t ext_adv_params_2M = {
.type = ESP_BLE_GAP_SET_EXT_ADV_PROP_CONNECTABLE,
.interval_min = 0x40,
.interval_max = 0x40,
.channel_map = ADV_CHNL_ALL,
.filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY,
.primary_phy = ESP_BLE_GAP_PHY_1M,
.max_skip = 0,
.secondary_phy = ESP_BLE_GAP_PHY_2M,
.sid = 1,
.scan_req_notif = false,
.own_addr_type = BLE_ADDR_TYPE_RANDOM,
};
static uint8_t raw_adv_data_1m[] = {
0x02, 0x01, 0x06,
0x02, 0x0a, 0xeb,
0x11, 0x09, 'E', 'S', 'P', '_', 'M', 'U', 'L', 'T', 'I', '_', 'A',
'D', 'V', '_', '1', 'M'
};
//ibeacon adv params
static uint8_t raw_adv_data_2m[] = {
//PREFIX
0x02, 0x01, 0x06, //ADV FLAGS
0x1A, 0xFF, //ADV HEADER
0x4C,0x00, //COMPANY ID
0x02, //ibeacon type
0x15, //ibeacon length
//UUID
0x00,0x11,0x22,0x33,
0x44,0x55,0x66,0x77,
0x88,0x99,0xAA,0xBB,
0xCC,0xDD,0xEE,0xFF,
//BEACON MAJOR AND MINOR
0x12,0x34,0x56,0x78,
//TX BYTE
0xBB,
};
static esp_ble_gap_ext_adv_t ext_adv[4] = {
// instance, duration, peroid
[0] = {0, 0, 0},
[1] = {1, 0, 0},
[2] = {2, 0, 0},
[3] = {3, 0, 0},
};
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_EXT_ADV_SET_RAND_ADDR_COMPLETE_EVT:
xSemaphoreGive(test_sem);
ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_EXT_ADV_SET_RAND_ADDR_COMPLETE_EVT, status %d", param->ext_adv_set_rand_addr.status);
break;
case ESP_GAP_BLE_EXT_ADV_SET_PARAMS_COMPLETE_EVT:
xSemaphoreGive(test_sem);
ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_EXT_ADV_SET_PARAMS_COMPLETE_EVT, status %d", param->ext_adv_set_params.status);
break;
case ESP_GAP_BLE_EXT_ADV_DATA_SET_COMPLETE_EVT:
xSemaphoreGive(test_sem);
ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_EXT_ADV_DATA_SET_COMPLETE_EVT, status %d", param->ext_adv_data_set.status);
break;
case ESP_GAP_BLE_EXT_SCAN_RSP_DATA_SET_COMPLETE_EVT:
xSemaphoreGive(test_sem);
ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_EXT_SCAN_RSP_DATA_SET_COMPLETE_EVT, status %d", param->scan_rsp_set.status);
break;
case ESP_GAP_BLE_EXT_ADV_START_COMPLETE_EVT:
xSemaphoreGive(test_sem);
ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_EXT_ADV_START_COMPLETE_EVT, status %d", param->ext_adv_start.status);
break;
case ESP_GAP_BLE_EXT_ADV_STOP_COMPLETE_EVT:
xSemaphoreGive(test_sem);
ESP_LOGI(LOG_TAG, "ESP_GAP_BLE_EXT_ADV_STOP_COMPLETE_EVT, status %d", param->ext_adv_stop.status);
break;
default:
break;
}
}
void app_main(void)
{
esp_err_t ret;
// Initialize NVS.
ret = nvs_flash_init();
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
ESP_ERROR_CHECK(nvs_flash_erase());
ret = nvs_flash_init();
}
ESP_ERROR_CHECK( ret );
ESP_ERROR_CHECK(esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT));
esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT();
ret = esp_bt_controller_init(&bt_cfg);
if (ret) {
ESP_LOGE(LOG_TAG, "%s initialize controller failed: %s\n", __func__, esp_err_to_name(ret));
return;
}
ret = esp_bt_controller_enable(ESP_BT_MODE_BLE);
if (ret) {
ESP_LOGE(LOG_TAG, "%s enable controller failed: %s\n", __func__, esp_err_to_name(ret));
return;
}
ret = esp_bluedroid_init();
if (ret) {
ESP_LOGE(LOG_TAG, "%s init bluetooth failed: %s\n", __func__, esp_err_to_name(ret));
return;
}
ret = esp_bluedroid_enable();
if (ret) {
ESP_LOGE(LOG_TAG, "%s enable bluetooth failed: %s\n", __func__, esp_err_to_name(ret));
return;
}
ret = esp_ble_gap_register_callback(gap_event_handler);
if (ret){
ESP_LOGE(LOG_TAG, "gap register error, error code = %x", ret);
return;
}
vTaskDelay(200 / portTICK_PERIOD_MS);
test_sem = xSemaphoreCreateBinary();
// 1M phy extend adv, Connectable advertising
FUNC_SEND_WAIT_SEM(esp_ble_gap_ext_adv_set_params(0, &ext_adv_params_1M), test_sem);
FUNC_SEND_WAIT_SEM(esp_ble_gap_ext_adv_set_rand_addr(0, addr_1m), test_sem);
FUNC_SEND_WAIT_SEM(esp_ble_gap_config_ext_adv_data_raw(0, sizeof(raw_adv_data_1m), &raw_adv_data_1m[0]), test_sem);
// 2M phy extend adv, Scannable advertising
FUNC_SEND_WAIT_SEM(esp_ble_gap_ext_adv_set_params(1, &ext_adv_params_2M), test_sem);
FUNC_SEND_WAIT_SEM(esp_ble_gap_ext_adv_set_rand_addr(1, addr_2m), test_sem);
FUNC_SEND_WAIT_SEM(esp_ble_gap_config_ext_adv_data_raw(1, sizeof(raw_adv_data_2m), &raw_adv_data_2m[0]), test_sem);
/*
// 1M phy legacy adv, ADV_IND
FUNC_SEND_WAIT_SEM(esp_ble_gap_ext_adv_set_params(2, &legacy_adv_params), test_sem);
FUNC_SEND_WAIT_SEM(esp_ble_gap_ext_adv_set_rand_addr(2, addr_legacy), test_sem);
FUNC_SEND_WAIT_SEM(esp_ble_gap_config_ext_adv_data_raw(2, sizeof(legacy_adv_data), &legacy_adv_data[0]), test_sem);
FUNC_SEND_WAIT_SEM(esp_ble_gap_config_ext_scan_rsp_data_raw(2, sizeof(legacy_scan_rsp_data), &legacy_scan_rsp_data[0]), test_sem);
// coded phy extend adv, Connectable advertising
FUNC_SEND_WAIT_SEM(esp_ble_gap_ext_adv_set_params(3, &ext_adv_params_coded), test_sem);
FUNC_SEND_WAIT_SEM(esp_ble_gap_ext_adv_set_rand_addr(3, addr_coded), test_sem);
FUNC_SEND_WAIT_SEM(esp_ble_gap_config_ext_scan_rsp_data_raw(3, sizeof(raw_scan_rsp_data_coded), &raw_scan_rsp_data_coded[0]), test_sem);
*/
// start all adv
FUNC_SEND_WAIT_SEM(esp_ble_gap_ext_adv_start(2, &ext_adv[0]), test_sem);
return;
}
From the code above, you can see that I am advertising 2 different packets:
esp_ble_gap_ext_adv_params_t ext_adv_params_1M = {
.type = ESP_BLE_GAP_SET_EXT_ADV_PROP_CONNECTABLE,
.interval_min = 0x30,
.interval_max = 0x30,
.channel_map = ADV_CHNL_ALL,
.filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY,
.primary_phy = ESP_BLE_GAP_PHY_1M,
.max_skip = 0,
.secondary_phy = ESP_BLE_GAP_PHY_1M,
.sid = 0,
.scan_req_notif = false,
.own_addr_type = BLE_ADDR_TYPE_RANDOM,
};
esp_ble_gap_ext_adv_params_t ext_adv_params_2M = {
.type = ESP_BLE_GAP_SET_EXT_ADV_PROP_CONNECTABLE,
.interval_min = 0x40,
.interval_max = 0x40,
.channel_map = ADV_CHNL_ALL,
.filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY,
.primary_phy = ESP_BLE_GAP_PHY_1M,
.max_skip = 0,
.secondary_phy = ESP_BLE_GAP_PHY_2M,
.sid = 1,
.scan_req_notif = false,
.own_addr_type = BLE_ADDR_TYPE_RANDOM,
};
static uint8_t raw_adv_data_1m[] = {
0x02, 0x01, 0x06,
0x02, 0x0a, 0xeb,
0x11, 0x09, 'E', 'S', 'P', '_', 'M', 'U', 'L', 'T', 'I', '_', 'A',
'D', 'V', '_', '1', 'M'
};
//ibeacon adv params
static uint8_t raw_adv_data_2m[] = {
//PREFIX
0x02, 0x01, 0x06, //ADV FLAGS
0x1A, 0xFF, //ADV HEADER
0x4C,0x00, //COMPANY ID
0x02, //ibeacon type
0x15, //ibeacon length
//UUID
0x00,0x11,0x22,0x33,
0x44,0x55,0x66,0x77,
0x88,0x99,0xAA,0xBB,
0xCC,0xDD,0xEE,0xFF,
//BEACON MAJOR AND MINOR
0x12,0x34,0x56,0x78,
//TX BYTE
0xBB,
};
adv_params_1M will be for my main app adv_params_2M will be for ibeacon.
I do not fully understand the deal with .primaty_phy and .secondary_phy parameters. Why are they necessary and how should I know what to use?
The ibeacon should be non connectable. If I set the ext_adv_params_2M type as ESP_BLE_GAP_SET_EXT_ADV_PROP_SCANNABLE
esp_ble_gap_ext_adv_params_t ext_adv_params_2M = {
.type = ESP_BLE_GAP_SET_EXT_ADV_PROP_SCANNABLE,
.interval_min = 0x40,
.interval_max = 0x40,
.channel_map = ADV_CHNL_ALL,
.filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY,
.primary_phy = ESP_BLE_GAP_PHY_1M,
.max_skip = 0,
.secondary_phy = ESP_BLE_GAP_PHY_2M,
.sid = 1,
.scan_req_notif = false,
.own_addr_type = BLE_ADDR_TYPE_RANDOM,
};
I get the following error:
I (657) MULTI_ADV_DEMO: ESP_GAP_BLE_EXT_ADV_SET_PARAMS_COMPLETE_EVT, status 0
I (657) MULTI_ADV_DEMO: ESP_GAP_BLE_EXT_ADV_SET_RAND_ADDR_COMPLETE_EVT, status 0
I (657) MULTI_ADV_DEMO: ESP_GAP_BLE_EXT_ADV_DATA_SET_COMPLETE_EVT, status 0
I (667) MULTI_ADV_DEMO: ESP_GAP_BLE_EXT_ADV_SET_PARAMS_COMPLETE_EVT, status 0
I (677) MULTI_ADV_DEMO: ESP_GAP_BLE_EXT_ADV_SET_RAND_ADDR_COMPLETE_EVT, status 0
E (687) BT_BTM: LE EA SetAdvData: cmd err=0x12
E (687) BT_HCI: CC evt: op=0x2037, status=0x12
I (697) MULTI_ADV_DEMO: ESP_GAP_BLE_EXT_ADV_DATA_SET_COMPLETE_EVT, status 7
E (697) BT_BTM: LE EA En=1: cmd err=0xc
E (707) BT_HCI: CC evt: op=0x2039, status=0xc
I (707) MULTI_ADV_DEMO: ESP_GAP_BLE_EXT_ADV_START_COMPLETE_EVT, status 7
What is the possible cause for this?
ret = esp_ble_gatts_register_callback(gatts_event_handler);
if (ret){
ESP_LOGE(GATTS_TABLE_TAG, "gatts register error, error code = %x", ret);
return;
}
ret = esp_ble_gap_register_callback(gap_event_handler);
if (ret){
ESP_LOGE(GATTS_TABLE_TAG, "gap register error, error code = %x", ret);
return;
}
ret = esp_ble_gatts_app_register(ESP_HEART_RATE_APP_ID);
if (ret){
ESP_LOGE(GATTS_TABLE_TAG, "gatts app register error, error code = %x", ret);
return;
}
Gatts event handler:
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) {
heart_rate_profile_tab[HEART_PROFILE_APP_IDX].gatts_if = gatts_if;
} else {
ESP_LOGI(GATTS_TABLE_TAG, "Reg app failed, app_id %04x, status %d\n",
param->reg.app_id,
param->reg.status);
return;
}
}
do {
int idx;
for (idx = 0; idx < HEART_PROFILE_NUM; idx++) {
if (gatts_if == ESP_GATT_IF_NONE || /* ESP_GATT_IF_NONE, not specify a certain gatt_if, need to call every profile cb function */
gatts_if == heart_rate_profile_tab[idx].gatts_if) {
if (heart_rate_profile_tab[idx].gatts_cb) {
heart_rate_profile_tab[idx].gatts_cb(event, gatts_if, param);
}
}
}
} while (0);
}
multi-adv.zip @krupis
Thanks for reporting, will close due to short of feedback, feel free to reopen with more updates.
Hello. I have been trying to find an answer to this for a while now, but sadly never managed to get anywhere with this. I would like to get clarification once and for all regarding this issue that I have been having for a while.
Issue description:
Need to have 2 separate Bluetooth profiles: First one would be used for communication with a BLE app (Same as gatt_server_sevice_table in the esp-idf examples). Second is used purely for advertising iBeacon packets (Same as ble_ibeacon example in the esp-idf examples).
So in short, the device should be able to advertise iBeacon packets as well as have another BLE profile that I can establish connection with via the lightBlue and have write/read characteristics there.
My questions:
_1. Is it possible to have 2 GAP profiles running at once on ESP32 devices?
If answer is no, are there any other workarounds how can this be achieved apart from putting an additional microcontroller that would be responsible for iBeaconing?
If the answer is yes, could someone point me in the right direction how can this be achieved? I have tried to merge ble_ibeacon and gatt_server_servicetable examples into one. The full source code is here:
/**** *
****/
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 "esp_bt.h"
include "esp_gap_ble_api.h"
include "esp_gatts_api.h"
include "esp_bt_main.h"
include "gatts_table_creat_demo.h"
include "esp_gatt_common_api.h"
//IBEACON STUFF
define ESP_UUID_IBEACON {0xFD, 0xA5, 0x06, 0x93, 0xA4, 0xE2, 0x4F, 0xB1, 0xAF, 0xCF, 0xC6, 0xEB, 0x07, 0x64, 0x78, 0x25}
define ESP_MAJOR_IBEACON 10167
define ESP_MINOR_IBEACON 61958
extern esp_ble_ibeacon_vendor_t vendor_config; static const char* DEMO_TAG = "IBEACON_DEMO"; const uint8_t uuid_zeros[ESP_UUID_LEN_128] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
define ESP_UUID_IBEACON {0xFD, 0xA5, 0x06, 0x93, 0xA4, 0xE2, 0x4F, 0xB1, 0xAF, 0xCF, 0xC6, 0xEB, 0x07, 0x64, 0x78, 0x25}
//BLE APP STUFF
define GATTS_TABLE_TAG "GATTS_TABLE_DEMO"
define PROFILE_NUM 1
define PROFILE_APP_IDX 0
define ESP_APP_ID 0x55
define SAMPLE_DEVICE_NAME "ESP_GATTS_DEMO"
define SVC_INST_ID 0
static esp_ble_adv_params_t ibeacon_adv_params = { .adv_int_min = 0x20, .adv_int_max = 0x40, .adv_type = ADV_TYPE_NONCONN_IND, .own_addr_type = BLE_ADDR_TYPE_PUBLIC, .channel_map = ADV_CHNL_ALL, .adv_filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY, };
esp_ble_ibeacon_head_t ibeacon_common_head = { .flags = {0x02, 0x01, 0x06}, .length = 0x1A, .type = 0xFF, .company_id = 0x004C, .beacon_type = 0x1502 };
/ Vendor part of iBeacon data/ esp_ble_ibeacon_vendor_t vendor_config = { .proximity_uuid = ESP_UUID_IBEACON, .major = ENDIAN_CHANGE_U16(ESP_MAJOR_IBEACON), //Major=ESP_MAJOR .minor = ENDIAN_CHANGE_U16(ESP_MINOR_IBEACON), //Minor=ESP_MINOR .measured_power = 0xC5 };
bool esp_ble_is_ibeacon_packet (uint8_t *adv_data, uint8_t adv_data_len){ bool result = false;
}
esp_err_t esp_ble_config_ibeacon_data (esp_ble_ibeacon_vendor_t vendor_config, esp_ble_ibeacon_t ibeacon_adv_data){ if ((vendor_config == NULL) || (ibeacon_adv_data == NULL) || (!memcmp(vendor_config->proximity_uuid, uuid_zeros, sizeof(uuid_zeros)))){ return ESP_ERR_INVALID_ARG; }
}
static void esp_gap_cb_ibeacon(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) { esp_err_t err;
if (IBEACON_MODE == IBEACON_SENDER)
endif
if (IBEACON_MODE == IBEACON_RECEIVER)
endif
}
//END OF IBEACON STUFF
/* The max length of characteristic value. When the GATT client performs a write or prepare write operation,
define GATTS_DEMO_CHAR_VAL_LEN_MAX 500
define PREPARE_BUF_MAX_SIZE 1024
define CHAR_DECLARATION_SIZE (sizeof(uint8_t))
define ADV_CONFIG_FLAG (1 << 0)
define SCAN_RSP_CONFIG_FLAG (1 << 1)
static uint8_t adv_config_done = 0;
uint16_t heart_rate_handle_table[HRS_IDX_NB];
typedef struct { uint8_t *prepare_buf; int prepare_len; } prepare_type_env_t;
static prepare_type_env_t prepare_write_env;
//#define CONFIG_SET_RAW_ADV_DATA
ifdef CONFIG_SET_RAW_ADV_DATA
static uint8_t raw_advdata[] = { / flags / 0x02, 0x01, 0x06, / tx power/ 0x02, 0x0a, 0xeb, / service uuid / 0x03, 0x03, 0xFF, 0x00, / device name / 0x0f, 0x09, 'E', 'S', 'P', '', 'G', 'A', 'T', 'T', 'S', '_', 'D','E', 'M', 'O' }; static uint8_t raw_scan_rsp_data[] = { / flags / 0x02, 0x01, 0x06, / tx power / 0x02, 0x0a, 0xeb, / service uuid / 0x03, 0x03, 0xFF,0x00 };
else
static uint8_t 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, 0xFF, 0x00, 0x00, 0x00, };
/ The length of adv data must be less than 31 bytes / static esp_ble_adv_data_t adv_data = { .set_scan_rsp = false, .include_name = true, .include_txpower = true, .min_interval = 0x0006, //slave connection min interval, Time = min_interval 1.25 msec .max_interval = 0x0010, //slave connection max interval, Time = max_interval 1.25 msec .appearance = 0x00, .manufacturer_len = 0, //TEST_MANUFACTURER_DATA_LEN, .p_manufacturer_data = NULL, //test_manufacturer, .service_data_len = 0, .p_service_data = NULL, .service_uuid_len = sizeof(service_uuid), .p_service_uuid = service_uuid, .flag = (ESP_BLE_ADV_FLAG_GEN_DISC | ESP_BLE_ADV_FLAG_BREDR_NOT_SPT), };
// scan response data static esp_ble_adv_data_t scan_rsp_data = { .set_scan_rsp = true, .include_name = true, .include_txpower = true, .min_interval = 0x0006, .max_interval = 0x0010, .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(service_uuid), .p_service_uuid = service_uuid, .flag = (ESP_BLE_ADV_FLAG_GEN_DISC | ESP_BLE_ADV_FLAG_BREDR_NOT_SPT), };
endif / CONFIG_SET_RAW_ADV_DATA /
static esp_ble_adv_params_t adv_params = { .adv_int_min = 0x20, .adv_int_max = 0x40, .adv_type = ADV_TYPE_IND, .own_addr_type = BLE_ADDR_TYPE_PUBLIC, .channel_map = ADV_CHNL_ALL, .adv_filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY, };
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_tab[PROFILE_NUM] = { [PROFILE_APP_IDX] = { .gatts_cb = gatts_profile_event_handler, .gatts_if = ESP_GATT_IF_NONE, / Not get the gatt_if, so initial is ESP_GATT_IF_NONE / }, };
/ Service / static const uint16_t GATTS_SERVICE_UUID_TEST = 0x00FF; static const uint16_t GATTS_CHAR_UUID_TEST_A = 0xFF01; static const uint16_t GATTS_CHAR_UUID_TEST_B = 0xFF02; static const uint16_t GATTS_CHAR_UUID_TEST_C = 0xFF03;
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_read = ESP_GATT_CHAR_PROP_BIT_READ; static const uint8_t char_prop_write = ESP_GATT_CHAR_PROP_BIT_WRITE; static const uint8_t char_prop_read_write_notify = ESP_GATT_CHAR_PROP_BIT_WRITE | ESP_GATT_CHAR_PROP_BIT_READ | ESP_GATT_CHAR_PROP_BIT_NOTIFY; static const uint8_t heart_measurement_ccc[2] = {0x00, 0x00}; static const uint8_t char_value[4] = {0x11, 0x22, 0x33, 0x44};
/ Full Database Description - Used to add attributes into the database / static const esp_gatts_attr_db_t gatt_db[HRS_IDX_NB] = { // Service Declaration [IDX_SVC] = {{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t )&primary_service_uuid, ESP_GATT_PERM_READ, sizeof(uint16_t), sizeof(GATTS_SERVICE_UUID_TEST), (uint8_t )&GATTS_SERVICE_UUID_TEST}},
};
static void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) { switch (event) {
ifdef CONFIG_SET_RAW_ADV_DATA
}
void example_prepare_write_event_env(esp_gatt_if_t gatts_if, prepare_type_env_t prepare_write_env, esp_ble_gatts_cb_param_t param) { ESP_LOGI(GATTS_TABLE_TAG, "prepare write, handle = %d, value len = %d", param->write.handle, param->write.len); esp_gatt_status_t status = ESP_GATT_OK; if (prepare_write_env->prepare_buf == NULL) { prepare_write_env->prepare_buf = (uint8_t )malloc(PREPARE_BUF_MAX_SIZE sizeof(uint8_t)); prepare_write_env->prepare_len = 0; if (prepare_write_env->prepare_buf == NULL) { ESP_LOGE(GATTS_TABLE_TAG, "%s, Gatt_server prep no mem", func); status = ESP_GATT_NO_RESOURCES; } } else { if(param->write.offset > PREPARE_BUF_MAX_SIZE) { status = ESP_GATT_INVALID_OFFSET; } else if ((param->write.offset + param->write.len) > PREPARE_BUF_MAX_SIZE) { status = ESP_GATT_INVALID_ATTR_LEN; } } /send response when param->write.need_rsp is true / if (param->write.need_rsp){ esp_gatt_rsp_t gatt_rsp = (esp_gatt_rsp_t )malloc(sizeof(esp_gatt_rsp_t)); if (gatt_rsp != NULL){ gatt_rsp->attr_value.len = param->write.len; gatt_rsp->attr_value.handle = param->write.handle; gatt_rsp->attr_value.offset = param->write.offset; gatt_rsp->attr_value.auth_req = ESP_GATT_AUTH_REQ_NONE; memcpy(gatt_rsp->attr_value.value, param->write.value, param->write.len); esp_err_t response_err = esp_ble_gatts_send_response(gatts_if, param->write.conn_id, param->write.trans_id, status, gatt_rsp); if (response_err != ESP_OK){ ESP_LOGE(GATTS_TABLE_TAG, "Send response error"); } free(gatt_rsp); }else{ ESP_LOGE(GATTS_TABLE_TAG, "%s, malloc failed", func); } } if (status != ESP_GATT_OK){ return; } memcpy(prepare_write_env->prepare_buf + param->write.offset, param->write.value, param->write.len); prepare_write_env->prepare_len += param->write.len;
}
void example_exec_write_event_env(prepare_type_env_t prepare_write_env, esp_ble_gatts_cb_param_t param){ if (param->exec_write.exec_write_flag == ESP_GATT_PREP_WRITE_EXEC && prepare_write_env->prepare_buf){ esp_log_buffer_hex(GATTS_TABLE_TAG, prepare_write_env->prepare_buf, prepare_write_env->prepare_len); }else{ ESP_LOGI(GATTS_TABLE_TAG,"ESP_GATT_PREP_WRITE_CANCEL"); } if (prepare_write_env->prepare_buf) { free(prepare_write_env->prepare_buf); prepare_write_env->prepare_buf = NULL; } prepare_write_env->prepare_len = 0; }
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:{ esp_err_t set_dev_name_ret = esp_ble_gap_set_device_name(SAMPLE_DEVICE_NAME); if (set_dev_name_ret){ ESP_LOGE(GATTS_TABLE_TAG, "set device name failed, error code = %x", set_dev_name_ret); }
ifdef CONFIG_SET_RAW_ADV_DATA
}
static void gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param) {
}
void app_main(void) { esp_err_t ret;
}
void app_main(void) { esp_err_t ret;
}
void app_main(void) { esp_err_t ret;
}