Bluetooth one server multiple clients issues

dizcza
Posts: 55
Joined: Tue Sep 07, 2021 6:59 pm

Bluetooth one server multiple clients issues

Postby dizcza » Tue Sep 21, 2021 8:12 am

I've set up an ESP32 board as an SPP BT Classic server that sends sensor data. Two Android phones connect to it. When only one device is connected to the server, everything works fine: I'm able to send and receive data successfully. As soon as another device joined, in a few seconds, I receive "rs_disc_pending=1" error

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
and the server stops sending the data to clients, although technically the connection(s) is still ongoing. More verbose BT logging outputs

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
...
Project menuconfig:
  • 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
I really don't know how many clients such configuration allows, two or four, but it should be able to handle at least two.

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
But I felt nothing when I looked at it. My server source code:

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)));
}
Full source code is available here: https://github.com/dizcza/esp32-sdpsens ... luetooth.c

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.

Who is online

Users browsing this forum: No registered users and 118 guests