Code: Select all
...
I (10803) BT: ESP_SPP_WRITE_EVT handle=129 cong=0
I (10813) BT: ESP_SPP_CONG_EVT handle=129 cong=1
I (10813) BT: ESP_SPP_WRITE_EVT handle=129 cong=1
W (13773) BT_APPL: new conn_srvc id:26, app_id:255
I (13773) BT: ESP_SPP_SRV_OPEN_EVT handle=385
E (14103) BT_BTM: tBTM_SEC_DEV:0x3ffd5034 rs_disc_pending=1
Code: Select all
...
I (136540) BT_APPL: bta_jv_pm_state_change(p_cb: 0x3ffc518c, handle: 0x81, busy/idle_state: 2, app_id: 255, conn_state: 6)
D (136560) BT_APPL: bta_dm_pm_cback: st(6), id(26), app(255)
D (136560) BT_APPL: bta_dm_pm_stop_timer:
D (136570) BT_APPL: bta_dm_pm_set_mode: srvcsid: 26, state: 6, j: 4
D (136570) BT_APPL: bta_dm_pm_set_mode dm_pm_timer:0, 7197
D (136580) BT_APPL: bta_dm_pm_set_mode dm_pm_timer:2, 7197
D (136580) BT_APPL: bta_dm_pm_set_mode dm_pm_timer:3, 7197
D (136590) BT_APPL: bta_dm_pm_set_mode dm_pm_timer:4, 7197
D (136590) BT_APPL: bta_dm_pm_set_mode dm_pm_timer:5, 7197
D (136600) BT_APPL: bta_dm_pm_set_mode dm_pm_timer:6, 7197
D (136610) BT_BTC: btc_transfer_context msg 1 14 33 0x3ffd5180
D (136610) BT_BTM: btm_acl_role_changed
D (136620) BT_BTM: BTM_SetLinkSuperTout
D (136620) BT_HCI: HCI Enqueue Comamnd opcode=0xc37
D (136630) BT_HCI: Receive packet event_code=0xe
D (136630) BT_HCI: btu_hcif_command_complete_evt
D (136630) BT_BTM: btm_acl_report_role_change
D (136640) BT_BTM: Role Switch Event: new_role 0x00, HCI Status 0x00, rs_st:0
E (136650) BT_BTM: tBTM_SEC_DEV:0x3ffd5a4c rs_disc_pending=1
D (136650) BT_BTM: btm_handle_to_acl_index: 128
D (136660) BT_BTM: btm_pm_proc_mode_change switched from ACTIVE to SNIFF.
D (136660) BT_BTM: btm mode change to active; check l2c_link for outgoing packets
D (136670) BT_L2CAP: l2c_link_check_send_pkts
D (136680) BT_L2CAP: partial_segment_being_sent=0,link_state=4,power_mode=0
D (136680) BT_L2CAP: RR scan pri=2, lcid=0x0041, q_cout=0
D (136690) BT_BTM: btm_cont_rswitch
...
- BT Classic BR/EDR Only
- (2) BR/EDR ACL Max Connections
- (0) BR/EDR Sync(SCO/eSCO) Max Connections
- BR/EDR Sync(SCO/eSCO) default data path (PCM)
- (4) BT/BLE MAX ACL CONNECTIONS
Looking deeply in the ESP-IDF components source code, I found the part where this error is shown:
Code: Select all
/* This is used to work around a controller bug that doesn't like Disconnect
** issued while there is a role switch in progress
*/
#ifndef BTM_DISC_DURING_RS
#define BTM_DISC_DURING_RS TRUE
#endif
...
#if BTM_DISC_DURING_RS == TRUE
/* If a disconnect is pending, issue it now that role switch has completed */
if ((p_dev_rec = btm_find_dev (p_bda)) != NULL) {
if (p_dev_rec->rs_disc_pending == BTM_SEC_DISC_PENDING) {
BTM_TRACE_WARNING("btm_acl_role_changed -> Issuing delayed HCI_Disconnect!!!\n");
btsnd_hcic_disconnect(p_dev_rec->hci_handle, HCI_ERR_PEER_USER);
}
BTM_TRACE_ERROR("tBTM_SEC_DEV:0x%x rs_disc_pending=%d\n",
(UINT32)p_dev_rec, p_dev_rec->rs_disc_pending);
p_dev_rec->rs_disc_pending = BTM_SEC_RS_NOT_PENDING; /* reset flag */
}
#endif
Code: Select all
static uint32_t spp_handle[BT_CONNECTIONS];
static uint32_t spp_hcnt = 0;
static SemaphoreHandle_t xSemaphoreServer;
static int client_id(uint32_t handle) {
int hidx;
for (hidx = 0; hidx < spp_hcnt; hidx++) {
if (spp_handle[hidx] == handle) break;
}
if (hidx == spp_hcnt) {
ESP_LOGW(TAG, "SPP client handle not found: %u", handle);
}
return hidx;
}
static void esp_spp_cb(esp_spp_cb_event_t event, esp_spp_cb_param_t *param)
{
switch (event) {
case ESP_SPP_INIT_EVT:
ESP_LOGI(TAG, "ESP_SPP_INIT_EVT");
const uint8_t bt_channel_any = 0;
esp_spp_start_srv(ESP_SPP_SEC_NONE, ESP_SPP_ROLE_SLAVE, bt_channel_any, "spp_server");
break;
case ESP_SPP_START_EVT:
ESP_LOGI(TAG, "ESP_SPP_START_EVT");
esp_bt_dev_set_device_name(BT_DEV_NAME);
esp_bt_gap_set_scan_mode(ESP_BT_CONNECTABLE, ESP_BT_GENERAL_DISCOVERABLE);
xSemaphoreGive(xSemaphoreServer);
break;
case ESP_SPP_DATA_IND_EVT:
ESP_LOGI(TAG, "ESP_SPP_DATA_IND_EVT handle=%u", param->data_ind.handle);
break;
case ESP_SPP_CONG_EVT:
ESP_LOGI(TAG, "ESP_SPP_CONG_EVT handle=%u cong=%d", param->cong.handle , param->cong.cong);
if (param->cong.cong == 0) {
xSemaphoreGive(xSemaphoreServer);
}
break;
case ESP_SPP_WRITE_EVT:
ESP_LOGI(TAG, "ESP_SPP_WRITE_EVT handle=%u cong=%d", param->write.handle , param->write.cong);
if (param->write.cong == 0) {
xSemaphoreGive(xSemaphoreServer);
}
break;
case ESP_SPP_SRV_OPEN_EVT:
ESP_LOGI(TAG, "ESP_SPP_SRV_OPEN_EVT handle=%u", param->srv_open.handle);
// the client is connected;
// & the SPP server is opened;
// & the handle is initialized;
xSemaphoreTake(xSemaphoreServer, portMAX_DELAY);
spp_handle[spp_hcnt++] = param->srv_open.handle;
xSemaphoreGive(xSemaphoreServer);
break;
case ESP_SPP_CLOSE_EVT:
ESP_LOGI(TAG, "ESP_SPP_CLOSE_EVT handle=%u", param->close.handle);
// The client left
xSemaphoreTake(xSemaphoreServer, portMAX_DELAY);
int hidx = client_id(param->close.handle);
while (hidx + 1 < spp_hcnt) {
// shift the remaining array left by 1
spp_handle[hidx] = spp_handle[hidx + 1];
hidx++;
}
spp_hcnt--;
xSemaphoreGive(xSemaphoreServer);
break;
default:
ESP_LOGI(TAG, "ESP_SPP event %d", event);
break;
}
}
static void esp_bt_gap_cb(esp_bt_gap_cb_event_t event, esp_bt_gap_cb_param_t *param)
{
switch (event) {
case ESP_BT_GAP_AUTH_CMPL_EVT:
if (param->auth_cmpl.stat == ESP_BT_STATUS_SUCCESS) {
ESP_LOGI(TAG, "auth success: %s", param->auth_cmpl.device_name);
esp_log_buffer_hex(TAG, param->auth_cmpl.bda, ESP_BD_ADDR_LEN);
} else {
ESP_LOGE(TAG, "auth failed (%d)", param->auth_cmpl.stat);
}
break;
case ESP_BT_GAP_MODE_CHG_EVT:
ESP_LOGI(TAG, "ESP_BT_GAP_MODE_CHG_EVT mode:%d", param->mode_chg.mode);
break;
case ESP_BT_GAP_CONFIG_EIR_DATA_EVT:
ESP_LOGI(TAG, "ESP_BT_GAP_CONFIG_EIR_DATA_EVT");
break;
default:
ESP_LOGI(TAG, "ESP_BT_GAP event: %d", event);
break;
}
return;
}
// this task is scheduled later on
static void bluetooth_send_buffered() {
char buffer[1024] = "abc";
int buf_pos = 3;
while (1) {
for (int client_id = 0; client_id < spp_hcnt; client_id++) {
xSemaphoreTake(xSemaphoreServer, portMAX_DELAY);
esp_spp_write(spp_handle[client_id], buf_pos, (uint8_t*) buffer);
}
vTaskDelay(pdMS_TO_TICKS(10));
}
}
static void bluetooth_init_peripheral() {
esp_err_t 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 );
// strip BLE part
ESP_ERROR_CHECK(esp_bt_controller_mem_release(ESP_BT_MODE_BLE));
esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_bt_controller_init(&bt_cfg));
ESP_ERROR_CHECK(esp_bt_controller_enable(ESP_BT_MODE_CLASSIC_BT));
ESP_ERROR_CHECK(esp_bluedroid_init());
ESP_ERROR_CHECK(esp_bluedroid_enable());
ESP_ERROR_CHECK(esp_bt_gap_register_callback(esp_bt_gap_cb));
ESP_ERROR_CHECK(esp_spp_register_callback(esp_spp_cb));
ESP_ERROR_CHECK(esp_spp_init(ESP_SPP_MODE_CB));
/* Set default parameters for Secure Simple Pairing */
esp_bt_sp_param_t param_type = ESP_BT_SP_IOCAP_MODE;
esp_bt_io_cap_t iocap = ESP_BT_IO_CAP_OUT;
ESP_ERROR_CHECK(esp_bt_gap_set_security_param(param_type, &iocap, sizeof(esp_bt_io_cap_t)));
}
Questions:
- A general question about event handlers: should I use "xSemaphoreTakeFromISR" & "xSemaphoreGiveFromISR" or "xSemaphoreTake" & "xSemaphoreGive" in an event handler callback function?
- Is the scheme above with taking and releasing a semaphore valid for BT Classic server-multiple-clients communication? What's the way to do it right? I've not found such an example.
- Will I get the same issue if I switch to BLE?
- What's the canonical way to handle one-server-multiple-clients communication in BLE? I've not found such an example either.