mirror of
https://github.com/espressif/esp-idf.git
synced 2026-10-02 11:10:54 +03:00
fix(nimble): Optimize Nimble throughput example for 1M/2M/S2/S8
This commit is contained in:
@@ -49,13 +49,13 @@
|
|||||||
#define WRITE_THROUGHPUT 2
|
#define WRITE_THROUGHPUT 2
|
||||||
#define NOTIFY_THROUGHPUT 3
|
#define NOTIFY_THROUGHPUT 3
|
||||||
|
|
||||||
#define READ_THROUGHPUT_PAYLOAD 510 /* MTU(512) - ATT read rsp header(1) - 1 (avoid Read Blob) */
|
#define READ_THROUGHPUT_PAYLOAD 497 /* 502 bytes ACL -> 2x 251 LL packets exactly (497 + 1 Read Rsp + 4 L2CAP) */
|
||||||
#define WRITE_THROUGHPUT_PAYLOAD 509 /* MTU(512) - ATT write cmd header(3) */
|
#define WRITE_THROUGHPUT_PAYLOAD 495 /* 502 bytes ACL -> 2x 251 LL packets exactly (495 + 3 Write Cmd + 4 L2CAP) */
|
||||||
#define LL_PACKET_TIME 2120
|
#define LL_PACKET_TIME 2120
|
||||||
#define LL_PACKET_LENGTH 251
|
#define LL_PACKET_LENGTH 251
|
||||||
static const char *tag = "blecent_throughput";
|
static const char *tag = "blecent_throughput";
|
||||||
static int blecent_gap_event(struct ble_gap_event *event, void *arg);
|
static int blecent_gap_event(struct ble_gap_event *event, void *arg);
|
||||||
static SemaphoreHandle_t xSemaphore;
|
|
||||||
static int mbuf_len_total;
|
static int mbuf_len_total;
|
||||||
static int failure_count;
|
static int failure_count;
|
||||||
static TaskHandle_t throughput_task_handle = NULL;
|
static TaskHandle_t throughput_task_handle = NULL;
|
||||||
@@ -66,28 +66,102 @@ static int mtu_def = 512;
|
|||||||
static ble_addr_t conn_addr;
|
static ble_addr_t conn_addr;
|
||||||
static uint16_t handle;
|
static uint16_t handle;
|
||||||
#define PHY_1M 0
|
#define PHY_1M 0
|
||||||
#if CONFIG_EXAMPLE_EXTENDED_ADV
|
|
||||||
static int current_phy_updated;
|
|
||||||
#define PHY_2M 1
|
#define PHY_2M 1
|
||||||
#define PHY_CODED_S2 2
|
#define PHY_CODED_S2 2
|
||||||
#define PHY_CODED_S8 3
|
#define PHY_CODED_S8 3
|
||||||
|
|
||||||
|
#if CONFIG_EXAMPLE_EXTENDED_ADV
|
||||||
|
static int current_phy_updated;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* State for callback-chained read throughput test */
|
||||||
|
static volatile bool read_test_active = false;
|
||||||
|
static uint16_t read_val_handle_g;
|
||||||
|
static uint16_t read_conn_handle_g;
|
||||||
|
static int read_count_g;
|
||||||
|
|
||||||
|
/* ==============================================================================
|
||||||
|
* Connection Parameter Tuning
|
||||||
|
* ==============================================================================
|
||||||
|
* itvl_min / itvl_max: Connection interval (time between connection events).
|
||||||
|
* Units are in 1.25ms. (e.g., 24 * 1.25ms = 30ms).
|
||||||
|
* latency: Slave latency (number of events the peripheral can skip).
|
||||||
|
* supervision_timeout: Time before link is considered lost. Units: 10ms.
|
||||||
|
* min_ce_len / max_ce_len: Connection Event length. max_ce_len = 0xFFFF tells
|
||||||
|
* the controller to use the ENTIRE connection interval
|
||||||
|
* for transmitting data, instead of cutting it short.
|
||||||
|
* ==============================================================================
|
||||||
|
*
|
||||||
|
* Write/Notify-optimized for 1M/2M: 30-50ms interval, full CE length. */
|
||||||
static struct ble_gap_upd_params conn_params = {
|
static struct ble_gap_upd_params conn_params = {
|
||||||
/** Minimum value for connection interval in 1.25ms units */
|
|
||||||
.itvl_min = CONFIG_EXAMPLE_CONN_ITVL_MIN,
|
.itvl_min = CONFIG_EXAMPLE_CONN_ITVL_MIN,
|
||||||
/** Maximum value for connection interval in 1.25ms units */
|
|
||||||
.itvl_max = CONFIG_EXAMPLE_CONN_ITVL_MAX,
|
.itvl_max = CONFIG_EXAMPLE_CONN_ITVL_MAX,
|
||||||
/** Connection latency */
|
|
||||||
.latency = CONFIG_EXAMPLE_CONN_LATENCY,
|
.latency = CONFIG_EXAMPLE_CONN_LATENCY,
|
||||||
/** Supervision timeout in 10ms units */
|
|
||||||
.supervision_timeout = CONFIG_EXAMPLE_CONN_TIMEOUT,
|
.supervision_timeout = CONFIG_EXAMPLE_CONN_TIMEOUT,
|
||||||
/** Minimum length of connection event in 0.625ms units */
|
.min_ce_len = 0,
|
||||||
.min_ce_len = CONFIG_EXAMPLE_CONN_CE_LEN_MIN,
|
.max_ce_len = 0xFFFF,
|
||||||
/** Maximum length of connection event in 0.625ms units */
|
|
||||||
.max_ce_len = CONFIG_EXAMPLE_CONN_CE_LEN_MAX,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* Write/Notify-optimized for Coded S2 */
|
||||||
|
static struct ble_gap_upd_params conn_params_coded_s2 = {
|
||||||
|
.itvl_min = 24, /* 30 ms */
|
||||||
|
.itvl_max = 32, /* 40 ms */
|
||||||
|
.latency = 0,
|
||||||
|
.supervision_timeout = CONFIG_EXAMPLE_CONN_TIMEOUT,
|
||||||
|
.min_ce_len = 0,
|
||||||
|
.max_ce_len = 0xFFFF,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Write/Notify-optimized for Coded S8 */
|
||||||
|
static struct ble_gap_upd_params conn_params_coded_s8 = {
|
||||||
|
.itvl_min = 60, /* 75 ms */
|
||||||
|
.itvl_max = 80, /* 100 ms */
|
||||||
|
.latency = 0,
|
||||||
|
.supervision_timeout = CONFIG_EXAMPLE_CONN_TIMEOUT,
|
||||||
|
.min_ce_len = 0,
|
||||||
|
.max_ce_len = 0xFFFF,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Read-optimized for 1M/2M: short interval for fast round-trips */
|
||||||
|
static struct ble_gap_upd_params conn_params_read = {
|
||||||
|
.itvl_min = 6, /* 7.5 ms */
|
||||||
|
.itvl_max = 8, /* 10 ms */
|
||||||
|
.latency = 0,
|
||||||
|
.supervision_timeout = CONFIG_EXAMPLE_CONN_TIMEOUT,
|
||||||
|
.min_ce_len = 0,
|
||||||
|
.max_ce_len = 0xFFFF,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Read-optimized for Coded S2 */
|
||||||
|
static struct ble_gap_upd_params conn_params_coded_s2_read = {
|
||||||
|
.itvl_min = 12, /* 15 ms */
|
||||||
|
.itvl_max = 16, /* 20 ms */
|
||||||
|
.latency = 0,
|
||||||
|
.supervision_timeout = CONFIG_EXAMPLE_CONN_TIMEOUT,
|
||||||
|
.min_ce_len = 0,
|
||||||
|
.max_ce_len = 0xFFFF,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Read-optimized for Coded S8 */
|
||||||
|
static struct ble_gap_upd_params conn_params_coded_s8_read = {
|
||||||
|
.itvl_min = 36, /* 45 ms */
|
||||||
|
.itvl_max = 40, /* 50 ms */
|
||||||
|
.latency = 0,
|
||||||
|
.supervision_timeout = CONFIG_EXAMPLE_CONN_TIMEOUT,
|
||||||
|
.min_ce_len = 0,
|
||||||
|
.max_ce_len = 0xFFFF,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void switch_conn_params(uint16_t conn_handle, const struct ble_gap_upd_params *params) {
|
||||||
|
int rc = ble_gap_update_params(conn_handle, params);
|
||||||
|
if (rc != 0) {
|
||||||
|
ESP_LOGE(tag, "Failed to update connection parameters: %d", rc);
|
||||||
|
} else {
|
||||||
|
ESP_LOGI(tag, "Requested connection parameter update");
|
||||||
|
vTaskDelay(500 / portTICK_PERIOD_MS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void ble_store_config_init(void);
|
void ble_store_config_init(void);
|
||||||
|
|
||||||
#if CONFIG_EXAMPLE_EXTENDED_ADV
|
#if CONFIG_EXAMPLE_EXTENDED_ADV
|
||||||
@@ -137,22 +211,20 @@ static int blecent_write(uint16_t conn_handle, uint16_t val_handle,
|
|||||||
start_time = esp_timer_get_time();
|
start_time = esp_timer_get_time();
|
||||||
|
|
||||||
while (write_time < test_time * 1000) {
|
while (write_time < test_time * 1000) {
|
||||||
/* Wait till the previous write is complete. For first time Semaphore
|
rc = ble_gattc_write_no_rsp_flat(conn_handle, val_handle, &value, sizeof value);
|
||||||
* is already available */
|
|
||||||
label:
|
|
||||||
rc = ble_gattc_write_no_rsp_flat(conn_handle, val_handle, &value, sizeof value);
|
|
||||||
|
|
||||||
if(rc == BLE_HS_ENOMEM) {
|
if (rc == BLE_HS_ENOMEM) {
|
||||||
vTaskDelay(2); /* Wait for buffers to free up and try again */
|
vTaskDelay(2); /* Wait for buffers to free up and try again */
|
||||||
goto label;
|
end_time = esp_timer_get_time();
|
||||||
}
|
write_time = (end_time - start_time) / 1000;
|
||||||
else if (rc != 0) {
|
continue;
|
||||||
ESP_LOGE(tag, "Error: Failed to write characteristic; rc=%d",rc);
|
} else if (rc != 0) {
|
||||||
goto err;
|
ESP_LOGE(tag, "Error: Failed to write characteristic; rc=%d", rc);
|
||||||
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
end_time = esp_timer_get_time();
|
end_time = esp_timer_get_time();
|
||||||
write_time = (end_time - start_time) / 1000 ;
|
write_time = (end_time - start_time) / 1000;
|
||||||
write_count += 1;
|
write_count += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -171,6 +243,11 @@ err:
|
|||||||
return ble_gap_terminate(peer->conn_handle, BLE_ERR_REM_USER_CONN_TERM);
|
return ble_gap_terminate(peer->conn_handle, BLE_ERR_REM_USER_CONN_TERM);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read callback that chains the next read immediately from within the
|
||||||
|
* NimBLE host context. This eliminates the FreeRTOS task-switch delay
|
||||||
|
* that previously caused each read to waste an extra connection event.
|
||||||
|
*/
|
||||||
static int
|
static int
|
||||||
blecent_repeat_read(uint16_t conn_handle,
|
blecent_repeat_read(uint16_t conn_handle,
|
||||||
const struct ble_gatt_error *error,
|
const struct ble_gatt_error *error,
|
||||||
@@ -178,55 +255,96 @@ blecent_repeat_read(uint16_t conn_handle,
|
|||||||
void *arg)
|
void *arg)
|
||||||
{
|
{
|
||||||
if (error->status == 0) {
|
if (error->status == 0) {
|
||||||
xSemaphoreGive(xSemaphore);
|
|
||||||
ESP_LOGD(tag, " attr_handle=%d value=", attr->handle);
|
ESP_LOGD(tag, " attr_handle=%d value=", attr->handle);
|
||||||
mbuf_len_total += OS_MBUF_PKTLEN(attr->om);
|
mbuf_len_total += OS_MBUF_PKTLEN(attr->om);
|
||||||
|
read_count_g++;
|
||||||
} else {
|
} else {
|
||||||
ESP_LOGE(tag, " Read failed, callback error code = %d", error->status );
|
ESP_LOGE(tag, " Read failed, callback error code = %d", error->status);
|
||||||
xSemaphoreGive(xSemaphore);
|
/* On error, stop chaining and wake up main task */
|
||||||
|
read_test_active = false;
|
||||||
|
if (throughput_task_handle) {
|
||||||
|
xTaskNotifyGive(throughput_task_handle);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
return error->status;
|
|
||||||
|
/* Chain next read immediately (zero task-switch overhead) */
|
||||||
|
if (read_test_active) {
|
||||||
|
int rc = ble_gattc_read(read_conn_handle_g, read_val_handle_g,
|
||||||
|
blecent_repeat_read, arg);
|
||||||
|
if (rc != 0) {
|
||||||
|
ESP_LOGE(tag, "Failed to chain read; rc=%d", rc);
|
||||||
|
read_test_active = false;
|
||||||
|
if (throughput_task_handle) {
|
||||||
|
xTaskNotifyGive(throughput_task_handle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* Test time expired — wake up main task to print results */
|
||||||
|
if (throughput_task_handle) {
|
||||||
|
xTaskNotifyGive(throughput_task_handle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int blecent_read(uint16_t conn_handle, uint16_t val_handle,
|
static int blecent_read(uint16_t conn_handle, uint16_t val_handle,
|
||||||
ble_gatt_attr_fn *cb, struct peer *peer, int test_time)
|
struct peer *peer, int test_time)
|
||||||
{
|
{
|
||||||
int rc, read_count = 0;
|
int rc;
|
||||||
int64_t start_time, end_time, read_time = 0;
|
int64_t start_time;
|
||||||
/* Keep track of number of bytes read from char */
|
|
||||||
|
/* Reset counters */
|
||||||
mbuf_len_total = 0;
|
mbuf_len_total = 0;
|
||||||
|
read_count_g = 0;
|
||||||
|
read_conn_handle_g = conn_handle;
|
||||||
|
read_val_handle_g = val_handle;
|
||||||
|
read_test_active = true;
|
||||||
|
|
||||||
|
/* Drain any stale notifications from a prior test that timed out */
|
||||||
|
ulTaskNotifyTake(pdTRUE, 0);
|
||||||
|
|
||||||
start_time = esp_timer_get_time();
|
start_time = esp_timer_get_time();
|
||||||
|
|
||||||
ESP_LOGD(tag, " Throughput read started :val_handle=%d test_time=%d", val_handle,
|
ESP_LOGD(tag, " Throughput read started :val_handle=%d test_time=%d", val_handle,
|
||||||
test_time);
|
test_time);
|
||||||
|
|
||||||
while (read_time < (test_time * 1000)) {
|
/* Kick off the first read — the callback will chain all subsequent reads */
|
||||||
/* Wait till the previous read is complete. For first time use Semaphore
|
rc = ble_gattc_read(conn_handle, val_handle,
|
||||||
* is already available */
|
blecent_repeat_read, (void *) peer);
|
||||||
xSemaphoreTake(xSemaphore, portMAX_DELAY);
|
if (rc != 0) {
|
||||||
|
ESP_LOGE(tag, "Error: Failed to start read chain; rc=%d", rc);
|
||||||
rc = ble_gattc_read(peer->conn_handle, val_handle,
|
read_test_active = false;
|
||||||
blecent_repeat_read, (void *) &peer);
|
goto err;
|
||||||
if (rc != 0) {
|
|
||||||
ESP_LOGE(tag, "Error: Failed to read characteristic; rc=%d",
|
|
||||||
rc);
|
|
||||||
goto err;
|
|
||||||
}
|
|
||||||
|
|
||||||
end_time = esp_timer_get_time();
|
|
||||||
read_time = (end_time - start_time) / 1000 ;
|
|
||||||
read_count += 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Application data throughput */
|
/* Sleep for the test duration while the callback chain runs autonomously.
|
||||||
|
* If the chain encounters an error (e.g. disconnect), it will notify us early. */
|
||||||
|
uint32_t notified = ulTaskNotifyTake(pdTRUE, pdMS_TO_TICKS(test_time * 1000));
|
||||||
|
|
||||||
|
if (notified == 0) {
|
||||||
|
/* Normal timeout, signal the chain to stop after the current in-flight read completes */
|
||||||
|
read_test_active = false;
|
||||||
|
/* Wait for the last callback to notify us (up to 5s safety timeout) */
|
||||||
|
ulTaskNotifyTake(pdTRUE, pdMS_TO_TICKS(5000));
|
||||||
|
} else {
|
||||||
|
/* Woken early by an error in the callback chain */
|
||||||
|
read_test_active = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t end_time = esp_timer_get_time();
|
||||||
|
int actual_secs = (int)((end_time - start_time) / 1000000);
|
||||||
|
if (actual_secs == 0) actual_secs = test_time;
|
||||||
|
|
||||||
|
/* Application data throughput */
|
||||||
printf("\n****************************************************************\n");
|
printf("\n****************************************************************\n");
|
||||||
ESP_LOGI(tag, "Application Read throughput = %d bps, Read op counter = %d",
|
ESP_LOGI(tag, "Application Read throughput = %d bps, Read op counter = %d",
|
||||||
(mbuf_len_total * 8) / (test_time), read_count);
|
(mbuf_len_total * 8) / actual_secs, read_count_g);
|
||||||
printf("\n****************************************************************\n");
|
printf("\n****************************************************************\n");
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
err:
|
err:
|
||||||
xSemaphoreGive(xSemaphore);
|
|
||||||
/* Terminate the connection. */
|
/* Terminate the connection. */
|
||||||
vTaskDelay(100 / portTICK_PERIOD_MS);
|
vTaskDelay(100 / portTICK_PERIOD_MS);
|
||||||
return ble_gap_terminate(peer->conn_handle, BLE_ERR_REM_USER_CONN_TERM);
|
return ble_gap_terminate(peer->conn_handle, BLE_ERR_REM_USER_CONN_TERM);
|
||||||
@@ -334,7 +452,7 @@ static void throughput_task(void *arg)
|
|||||||
printf(" | Phy mode: Enter value in 0 for 1M, 1 for 2M ,2 for Coded S2, |\n");
|
printf(" | Phy mode: Enter value in 0 for 1M, 1 for 2M ,2 for Coded S2, |\n");
|
||||||
printf(" | 3 for Coded S8. |\n");
|
printf(" | 3 for Coded S8. |\n");
|
||||||
printf(" | |\n");
|
printf(" | |\n");
|
||||||
printf(" | e.g. throughput read 600 3 |\n");
|
printf(" | e.g. throughput read 60 3 |\n");
|
||||||
printf(" | |\n");
|
printf(" | |\n");
|
||||||
printf(" | ** Enter 'throughput read 60 0' for reading char for 60 seconds on 1M Phy |\n");
|
printf(" | ** Enter 'throughput read 60 0' for reading char for 60 seconds on 1M Phy |\n");
|
||||||
printf(" | OR 'throughput write 60 1' for writing to char for 60 seconds on 2M Phy |\n");
|
printf(" | OR 'throughput write 60 1' for writing to char for 60 seconds on 2M Phy |\n");
|
||||||
@@ -365,12 +483,46 @@ static void throughput_task(void *arg)
|
|||||||
vTaskDelay(100 / portTICK_PERIOD_MS);
|
vTaskDelay(100 / portTICK_PERIOD_MS);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Send a 1-byte control message to the peripheral's read/write char
|
||||||
|
* to tell it which PHY coding we are testing. This ensures the peripheral
|
||||||
|
* perfectly mirrors our PHY preference (1M, 2M, S2, or S8). */
|
||||||
|
if (test_data[2] >= 0 && test_data[2] <= 3) {
|
||||||
|
const struct peer_chr *cmd_chr = peer_chr_find_uuid(peer,
|
||||||
|
THRPT_UUID_DECLARE(THRPT_SVC),
|
||||||
|
THRPT_UUID_DECLARE(THRPT_CHR_READ_WRITE));
|
||||||
|
if (cmd_chr != NULL) {
|
||||||
|
uint8_t phy_cmd = test_data[2];
|
||||||
|
rc = ble_gattc_write_no_rsp_flat(conn_handle, cmd_chr->chr.val_handle, &phy_cmd, 1);
|
||||||
|
if (rc != 0) {
|
||||||
|
ESP_LOGW(tag, "Failed to send PHY cmd to peripheral; rc=%d", rc);
|
||||||
|
} else {
|
||||||
|
vTaskDelay(200 / portTICK_PERIOD_MS); /* Give peripheral time to apply PHY */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
/* Extended advertising is disabled, only 1M PHY is available */
|
||||||
|
if (test_data[2] != PHY_1M) {
|
||||||
|
ESP_LOGW(tag, "Extended advertising disabled; forcing PHY to 1M (ignoring user selection %d)", test_data[2]);
|
||||||
|
test_data[2] = PHY_1M;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
switch (test_data[0]) {
|
switch (test_data[0]) {
|
||||||
|
|
||||||
case READ_THROUGHPUT:
|
case READ_THROUGHPUT:
|
||||||
|
/* PHY-aware read connection params: short interval for fast
|
||||||
|
* round-trips on 1M/2M, longer for coded PHY */
|
||||||
|
if (test_data[2] == PHY_CODED_S2) {
|
||||||
|
switch_conn_params(conn_handle, &conn_params_coded_s2_read);
|
||||||
|
} else if (test_data[2] == PHY_CODED_S8) {
|
||||||
|
switch_conn_params(conn_handle, &conn_params_coded_s8_read);
|
||||||
|
} else {
|
||||||
|
switch_conn_params(conn_handle, &conn_params_read);
|
||||||
|
}
|
||||||
|
|
||||||
/* Read the characteristic supporting long read support
|
/* Read the characteristic supporting long read support
|
||||||
* `THRPT_LONG_CHR_READ_WRITE` (0x000b) */
|
* `THRPT_LONG_CHR_READ_WRITE` (0x000b) */
|
||||||
chr = peer_chr_find_uuid(peer,
|
chr = peer_chr_find_uuid(peer,
|
||||||
@@ -379,12 +531,12 @@ static void throughput_task(void *arg)
|
|||||||
if (chr == NULL) {
|
if (chr == NULL) {
|
||||||
ESP_LOGE(tag, "Peer does not support "
|
ESP_LOGE(tag, "Peer does not support "
|
||||||
"LONG_READ (0x000b) characteristic ");
|
"LONG_READ (0x000b) characteristic ");
|
||||||
break;
|
goto read_cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (test_data[1] > 0) {
|
if (test_data[1] > 0) {
|
||||||
rc = blecent_read(conn_handle, chr->chr.val_handle,
|
rc = blecent_read(conn_handle, chr->chr.val_handle,
|
||||||
blecent_repeat_read, (void *) peer, test_data[1]);
|
(void *) peer, test_data[1]);
|
||||||
if (rc != 0) {
|
if (rc != 0) {
|
||||||
ESP_LOGE(tag, "Error while reading from GATTS; rc = %d", rc);
|
ESP_LOGE(tag, "Error while reading from GATTS; rc = %d", rc);
|
||||||
/* Delete task on critical error (connection lost or fatal error) */
|
/* Delete task on critical error (connection lost or fatal error) */
|
||||||
@@ -398,9 +550,28 @@ static void throughput_task(void *arg)
|
|||||||
} else {
|
} else {
|
||||||
ESP_LOGE(tag, "Please enter non-zero value for test time in seconds!!");
|
ESP_LOGE(tag, "Please enter non-zero value for test time in seconds!!");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
read_cleanup:
|
||||||
|
/* Restore write-optimized params after the read test */
|
||||||
|
ESP_LOGI(tag, "Restoring WRITE-optimized conn params");
|
||||||
|
if (test_data[2] == PHY_CODED_S2) {
|
||||||
|
switch_conn_params(conn_handle, &conn_params_coded_s2);
|
||||||
|
} else if (test_data[2] == PHY_CODED_S8) {
|
||||||
|
switch_conn_params(conn_handle, &conn_params_coded_s8);
|
||||||
|
} else {
|
||||||
|
switch_conn_params(conn_handle, &conn_params);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case WRITE_THROUGHPUT:
|
case WRITE_THROUGHPUT:
|
||||||
|
if (test_data[2] == PHY_CODED_S2) {
|
||||||
|
switch_conn_params(conn_handle, &conn_params_coded_s2);
|
||||||
|
} else if (test_data[2] == PHY_CODED_S8) {
|
||||||
|
switch_conn_params(conn_handle, &conn_params_coded_s8);
|
||||||
|
} else {
|
||||||
|
switch_conn_params(conn_handle, &conn_params);
|
||||||
|
}
|
||||||
|
|
||||||
chr = peer_chr_find_uuid(peer,
|
chr = peer_chr_find_uuid(peer,
|
||||||
THRPT_UUID_DECLARE(THRPT_SVC),
|
THRPT_UUID_DECLARE(THRPT_SVC),
|
||||||
THRPT_UUID_DECLARE(THRPT_CHR_READ_WRITE));
|
THRPT_UUID_DECLARE(THRPT_CHR_READ_WRITE));
|
||||||
@@ -428,6 +599,14 @@ static void throughput_task(void *arg)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case NOTIFY_THROUGHPUT:
|
case NOTIFY_THROUGHPUT:
|
||||||
|
if (test_data[2] == PHY_CODED_S2) {
|
||||||
|
switch_conn_params(conn_handle, &conn_params_coded_s2);
|
||||||
|
} else if (test_data[2] == PHY_CODED_S8) {
|
||||||
|
switch_conn_params(conn_handle, &conn_params_coded_s8);
|
||||||
|
} else {
|
||||||
|
switch_conn_params(conn_handle, &conn_params);
|
||||||
|
}
|
||||||
|
|
||||||
chr = peer_chr_find_uuid(peer,
|
chr = peer_chr_find_uuid(peer,
|
||||||
THRPT_UUID_DECLARE(THRPT_SVC),
|
THRPT_UUID_DECLARE(THRPT_SVC),
|
||||||
THRPT_UUID_DECLARE(THRPT_CHR_NOTIFY));
|
THRPT_UUID_DECLARE(THRPT_CHR_NOTIFY));
|
||||||
@@ -463,6 +642,20 @@ static void throughput_task(void *arg)
|
|||||||
test_data[1]);
|
test_data[1]);
|
||||||
}
|
}
|
||||||
vTaskDelay(test_data[1]*1000 / portTICK_PERIOD_MS);
|
vTaskDelay(test_data[1]*1000 / portTICK_PERIOD_MS);
|
||||||
|
|
||||||
|
/* Unsubscribe so the next notify test triggers a fresh
|
||||||
|
* BLE_GAP_EVENT_SUBSCRIBE on the peripheral (cur_notify 0→1) */
|
||||||
|
{
|
||||||
|
uint8_t unsub_value[2] = {0, 0};
|
||||||
|
rc = ble_gattc_write_flat(conn_handle, dsc->dsc.handle,
|
||||||
|
unsub_value, sizeof unsub_value,
|
||||||
|
NULL, NULL);
|
||||||
|
if (rc != 0) {
|
||||||
|
ESP_LOGW(tag, "Unsubscribe failed; rc=%d (non-fatal)", rc);
|
||||||
|
} else {
|
||||||
|
ESP_LOGI(tag, "Unsubscribed from notifications");
|
||||||
|
}
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@@ -656,7 +849,7 @@ blecent_should_connect(const struct ble_gap_disc_desc *disc)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ESP_LOGI(tag, "connect; fields.num_uuids128 =%d", fields.num_uuids128);
|
ESP_LOGD(tag, "connect; fields.num_uuids128 =%d", fields.num_uuids128);
|
||||||
for (i = 0; i < fields.num_uuids128; i++) {
|
for (i = 0; i < fields.num_uuids128; i++) {
|
||||||
if ((memcmp(&fields.uuids128[i], THRPT_UUID_DECLARE(THRPT_SVC),
|
if ((memcmp(&fields.uuids128[i], THRPT_UUID_DECLARE(THRPT_SVC),
|
||||||
sizeof(ble_uuid128_t))) == 0 ) {
|
sizeof(ble_uuid128_t))) == 0 ) {
|
||||||
@@ -759,7 +952,7 @@ blecent_gap_event(struct ble_gap_event *event, void *arg)
|
|||||||
|
|
||||||
switch (event->type) {
|
switch (event->type) {
|
||||||
case BLE_GAP_EVENT_DISC:
|
case BLE_GAP_EVENT_DISC:
|
||||||
ESP_LOGI(tag, "Event DISC ");
|
ESP_LOGD(tag, "Event DISC ");
|
||||||
rc = ble_hs_adv_parse_fields(&fields, event->disc.data,
|
rc = ble_hs_adv_parse_fields(&fields, event->disc.data,
|
||||||
event->disc.length_data);
|
event->disc.length_data);
|
||||||
if (rc != 0) {
|
if (rc != 0) {
|
||||||
@@ -880,6 +1073,11 @@ blecent_gap_event(struct ble_gap_event *event, void *arg)
|
|||||||
/* Attribute data is contained in event->notify_rx.attr_data. */
|
/* Attribute data is contained in event->notify_rx.attr_data. */
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
case BLE_GAP_EVENT_CONN_UPDATE:
|
||||||
|
ESP_LOGI(tag, "connection updated; status=%d",
|
||||||
|
event->conn_update.status);
|
||||||
|
return 0;
|
||||||
|
|
||||||
case BLE_GAP_EVENT_MTU:
|
case BLE_GAP_EVENT_MTU:
|
||||||
ESP_LOGI(tag, "mtu update event; conn_handle = %d cid = %d mtu = %d",
|
ESP_LOGI(tag, "mtu update event; conn_handle = %d cid = %d mtu = %d",
|
||||||
event->mtu.conn_handle,
|
event->mtu.conn_handle,
|
||||||
@@ -890,7 +1088,6 @@ blecent_gap_event(struct ble_gap_event *event, void *arg)
|
|||||||
#if CONFIG_EXAMPLE_EXTENDED_ADV
|
#if CONFIG_EXAMPLE_EXTENDED_ADV
|
||||||
case BLE_GAP_EVENT_EXT_DISC:
|
case BLE_GAP_EVENT_EXT_DISC:
|
||||||
/* An advertisement report was received during GAP discovery. */
|
/* An advertisement report was received during GAP discovery. */
|
||||||
|
|
||||||
blecent_connect_if_interesting(&event->ext_disc);
|
blecent_connect_if_interesting(&event->ext_disc);
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
@@ -977,12 +1174,9 @@ blecent_on_sync(void)
|
|||||||
void blecent_host_task(void *param)
|
void blecent_host_task(void *param)
|
||||||
{
|
{
|
||||||
ESP_LOGI(tag, "BLE Host Task Started");
|
ESP_LOGI(tag, "BLE Host Task Started");
|
||||||
xSemaphore = xSemaphoreCreateBinary();
|
|
||||||
xSemaphoreGive(xSemaphore);
|
|
||||||
|
|
||||||
/* This function will return only when nimble_port_stop() is executed */
|
/* This function will return only when nimble_port_stop() is executed */
|
||||||
nimble_port_run();
|
nimble_port_run();
|
||||||
vSemaphoreDelete(xSemaphore);
|
|
||||||
nimble_port_freertos_deinit();
|
nimble_port_freertos_deinit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+26
-5
@@ -2,14 +2,35 @@
|
|||||||
# in this example and some misc buffer sizes are increased
|
# in this example and some misc buffer sizes are increased
|
||||||
|
|
||||||
#
|
#
|
||||||
# BT config
|
# BT config (universal across all ESP32 variants)
|
||||||
#
|
#
|
||||||
CONFIG_BT_ENABLED=y
|
CONFIG_BT_ENABLED=y
|
||||||
CONFIG_BT_BLUEDROID_ENABLED=n
|
CONFIG_BT_BLUEDROID_ENABLED=n
|
||||||
CONFIG_BT_NIMBLE_ENABLED=y
|
CONFIG_BT_NIMBLE_ENABLED=y
|
||||||
|
|
||||||
|
# === BT 5.0 / 2M PHY support ===
|
||||||
|
# Ignored automatically on classic ESP32 which lacks BT 5.0
|
||||||
|
CONFIG_BT_NIMBLE_EXT_ADV=n
|
||||||
|
CONFIG_BT_NIMBLE_50_FEATURE_SUPPORT=y
|
||||||
|
CONFIG_EXAMPLE_EXTENDED_ADV=n
|
||||||
|
|
||||||
|
# === ATT / GATT ===
|
||||||
CONFIG_BT_NIMBLE_ATT_PREFERRED_MTU=512
|
CONFIG_BT_NIMBLE_ATT_PREFERRED_MTU=512
|
||||||
CONFIG_BT_NIMBLE_TRANSPORT_ACL_FROM_LL_COUNT=20
|
|
||||||
CONFIG_BT_NIMBLE_TRANSPORT_EVT_SIZE=255
|
# === Memory Pool Tuning ===
|
||||||
|
CONFIG_BT_NIMBLE_MAX_CONNECTIONS=1
|
||||||
CONFIG_BT_NIMBLE_MSYS_1_BLOCK_COUNT=50
|
CONFIG_BT_NIMBLE_MSYS_1_BLOCK_COUNT=50
|
||||||
CONFIG_BT_NIMBLE_LOG_LEVEL=4
|
CONFIG_BT_NIMBLE_MSYS_2_BLOCK_SIZE=592
|
||||||
CONFIG_BT_NIMBLE_LOG_LEVEL_NONE=y
|
CONFIG_BT_NIMBLE_MSYS_2_BLOCK_COUNT=40
|
||||||
|
|
||||||
|
# Increase controller-to-host buffers
|
||||||
|
CONFIG_BT_NIMBLE_TRANSPORT_ACL_FROM_LL_COUNT=40
|
||||||
|
CONFIG_BT_NIMBLE_TRANSPORT_EVT_SIZE=255
|
||||||
|
|
||||||
|
# === Connection Parameters (write/notify optimal; code switches dynamically for reads) ===
|
||||||
|
CONFIG_EXAMPLE_CONN_ITVL_MIN=24
|
||||||
|
CONFIG_EXAMPLE_CONN_ITVL_MAX=40
|
||||||
|
|
||||||
|
# Disable NimBLE INFO logging for throughput tests
|
||||||
|
CONFIG_BT_NIMBLE_LOG_LEVEL_WARNING=y
|
||||||
|
CONFIG_BT_NIMBLE_LOG_LEVEL=2
|
||||||
|
|||||||
+5
@@ -0,0 +1,5 @@
|
|||||||
|
# === High-Performance Tuning ===
|
||||||
|
# This config is only applied when building for the ESP32-C6 target
|
||||||
|
# It is necessary for the C6 to reach 750+ kbps and 1.3+ Mbps throughput.
|
||||||
|
CONFIG_COMPILER_OPTIMIZATION_PERF=y
|
||||||
|
CONFIG_FREERTOS_HZ=1000
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
|
* SPDX-FileCopyrightText: 2015-2026 Espressif Systems (Shanghai) CO LTD
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
*/
|
*/
|
||||||
@@ -33,8 +33,8 @@
|
|||||||
#define THRPT_CHR_NOTIFY 0x000a
|
#define THRPT_CHR_NOTIFY 0x000a
|
||||||
#define THRPT_LONG_CHR_READ_WRITE 0x000b
|
#define THRPT_LONG_CHR_READ_WRITE 0x000b
|
||||||
|
|
||||||
#define READ_THROUGHPUT_PAYLOAD 510 /* MTU(512) - ATT read rsp header(1) - 1 (avoid Read Blob) */
|
#define READ_THROUGHPUT_PAYLOAD 497 /* 502 bytes ACL -> 2x 251 LL packets exactly (497 + 1 Read Rsp + 4 L2CAP) */
|
||||||
#define WRITE_THROUGHPUT_PAYLOAD 509 /* MTU(512) - ATT write cmd header(3) */
|
#define WRITE_THROUGHPUT_PAYLOAD 495 /* 502 bytes ACL -> 2x 251 LL packets exactly (495 + 3 Write Cmd + 4 L2CAP) */
|
||||||
|
|
||||||
static const char *tag = "bleprph_throughput";
|
static const char *tag = "bleprph_throughput";
|
||||||
|
|
||||||
@@ -142,6 +142,32 @@ gatt_svr_read_write_long_test(uint16_t conn_handle, uint16_t attr_handle,
|
|||||||
|
|
||||||
case THRPT_CHR_READ_WRITE:
|
case THRPT_CHR_READ_WRITE:
|
||||||
if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR) {
|
if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR) {
|
||||||
|
uint16_t om_len = OS_MBUF_PKTLEN(ctxt->om);
|
||||||
|
/* If the central sends exactly 1 byte, it is a PHY control command */
|
||||||
|
if (om_len == 1) {
|
||||||
|
uint8_t requested_phy;
|
||||||
|
os_mbuf_copydata(ctxt->om, 0, 1, &requested_phy);
|
||||||
|
rc = 0;
|
||||||
|
if (requested_phy == 0) { /* 0 = PHY_1M */
|
||||||
|
rc = ble_gap_set_prefered_le_phy(conn_handle, BLE_HCI_LE_PHY_1M_PREF_MASK, BLE_HCI_LE_PHY_1M_PREF_MASK, 0);
|
||||||
|
ESP_LOGI(tag, "Central requested 1M PHY via GATT command");
|
||||||
|
} else if (requested_phy == 1) { /* 1 = PHY_2M */
|
||||||
|
rc = ble_gap_set_prefered_le_phy(conn_handle, BLE_HCI_LE_PHY_2M_PREF_MASK, BLE_HCI_LE_PHY_2M_PREF_MASK, 0);
|
||||||
|
ESP_LOGI(tag, "Central requested 2M PHY via GATT command");
|
||||||
|
} else if (requested_phy == 2) { /* 2 = PHY_CODED_S2 */
|
||||||
|
rc = ble_gap_set_prefered_le_phy(conn_handle, BLE_HCI_LE_PHY_CODED_PREF_MASK, BLE_HCI_LE_PHY_CODED_PREF_MASK, 0x01);
|
||||||
|
ESP_LOGI(tag, "Central requested S2 PHY via GATT command");
|
||||||
|
} else if (requested_phy == 3) { /* 3 = PHY_CODED_S8 */
|
||||||
|
rc = ble_gap_set_prefered_le_phy(conn_handle, BLE_HCI_LE_PHY_CODED_PREF_MASK, BLE_HCI_LE_PHY_CODED_PREF_MASK, 0x02);
|
||||||
|
ESP_LOGI(tag, "Central requested S8 PHY via GATT command");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rc != 0) {
|
||||||
|
ESP_LOGE(tag, "Failed to set preferred LE PHY; rc=%d", rc);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
rc = gatt_svr_chr_write(conn_handle, attr_handle,
|
rc = gatt_svr_chr_write(conn_handle, attr_handle,
|
||||||
ctxt->om, 0,
|
ctxt->om, 0,
|
||||||
sizeof gatt_svr_thrpt_static_short_val,
|
sizeof gatt_svr_thrpt_static_short_val,
|
||||||
|
|||||||
@@ -30,16 +30,16 @@ static uint8_t s_current_phy;
|
|||||||
|
|
||||||
static const char *device_name = "nimble_prph";
|
static const char *device_name = "nimble_prph";
|
||||||
|
|
||||||
#define NOTIFY_THROUGHPUT_PAYLOAD 509 /* MTU(512) - ATT notify header(3) */
|
#define NOTIFY_THROUGHPUT_PAYLOAD 495 /* Exactly aligns with 2 LL packets */
|
||||||
#define MIN_REQUIRED_MBUF 2 /* Assuming payload of 500Bytes and each mbuf can take 292Bytes. */
|
#define MIN_REQUIRED_MBUF 10 /* Reserve mbufs for incoming ATT packets */
|
||||||
#define NOTIFY_PIPELINE_DEPTH 15 /* Number of notifications to keep in flight for throughput */
|
#define NOTIFY_PIPELINE_DEPTH 8 /* Number of notifications to keep in flight */
|
||||||
#define PREFERRED_MTU_VALUE 512
|
#define PREFERRED_MTU_VALUE 512
|
||||||
#define LL_PACKET_TIME 2120
|
#define LL_PACKET_TIME 2120
|
||||||
#define LL_PACKET_LENGTH 251
|
#define LL_PACKET_LENGTH 251
|
||||||
#define MTU_DEF 512
|
#define MTU_DEF 512
|
||||||
|
|
||||||
static const char *tag = "bleprph_throughput";
|
static const char *tag = "bleprph_throughput";
|
||||||
static SemaphoreHandle_t notify_sem;
|
static TaskHandle_t notify_task_handle = NULL;
|
||||||
static bool notify_state;
|
static bool notify_state;
|
||||||
static int notify_test_time = 60;
|
static int notify_test_time = 60;
|
||||||
static uint16_t conn_handle;
|
static uint16_t conn_handle;
|
||||||
@@ -260,12 +260,13 @@ notify_task(void *arg)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
while (notify_time < (notify_test_time * 1000)) {
|
while (notify_state && notify_time < (notify_test_time * 1000)) {
|
||||||
/* We are anyway using counting semaphore for sending
|
ulTaskNotifyTake(pdFALSE, portMAX_DELAY);
|
||||||
* notifications. So hopefully not much waiting period will be
|
|
||||||
* introduced before sending a new notification. Revisit this
|
/* Stop immediately if central unsubscribed */
|
||||||
* counter if need to do away with semaphore waiting. XXX */
|
if (!notify_state) {
|
||||||
xSemaphoreTake(notify_sem, portMAX_DELAY);
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if (dummy == 200) {
|
if (dummy == 200) {
|
||||||
dummy = 0;
|
dummy = 0;
|
||||||
@@ -284,17 +285,22 @@ notify_task(void *arg)
|
|||||||
|
|
||||||
rc = ble_gatts_notify_custom(conn_handle, notify_handle, om);
|
rc = ble_gatts_notify_custom(conn_handle, notify_handle, om);
|
||||||
if (rc != 0) {
|
if (rc != 0) {
|
||||||
ESP_LOGE(tag, "Error while sending notification; rc = %d", rc);
|
if (rc == BLE_HS_ENOMEM) {
|
||||||
|
ESP_LOGD(tag, "Notification queue full (expected backpressure)");
|
||||||
|
} else {
|
||||||
|
ESP_LOGE(tag, "Error while sending notification; rc = %d", rc);
|
||||||
|
}
|
||||||
notify_count -= 1;
|
notify_count -= 1;
|
||||||
xSemaphoreGive(notify_sem);
|
|
||||||
/* Yield to let mbufs free up */
|
/* Yield to let mbufs free up */
|
||||||
vTaskDelay(1);
|
vTaskDelay(1);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
xSemaphoreGive(notify_sem);
|
if (notify_task_handle) {
|
||||||
notify_count -= 1;
|
xTaskNotifyGive(notify_task_handle);
|
||||||
/* Yield briefly to let mbufs free up */
|
}
|
||||||
vTaskDelay(1);
|
notify_count -= 1;
|
||||||
|
/* Yield briefly to let mbufs free up */
|
||||||
|
vTaskDelay(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
end_time = esp_timer_get_time();
|
end_time = esp_timer_get_time();
|
||||||
@@ -302,13 +308,20 @@ notify_task(void *arg)
|
|||||||
notify_count += 1;
|
notify_count += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
printf("\n*********************************\n");
|
/* Use actual elapsed time for accurate throughput calculation */
|
||||||
ESP_LOGI(tag, "Notify throughput = %d bps, count = %d",
|
{
|
||||||
(notify_count * NOTIFY_THROUGHPUT_PAYLOAD * 8) / notify_test_time, notify_count);
|
int actual_secs = (int)(notify_time / 1000);
|
||||||
printf("\n*********************************\n");
|
if (actual_secs > 0 && notify_count > 0) {
|
||||||
ESP_LOGI(tag, " Notification test complete for stipulated time of %d sec", notify_test_time);
|
printf("\n*********************************\n");
|
||||||
|
ESP_LOGI(tag, "Notify throughput = %d bps, count = %d",
|
||||||
|
(int)(((uint64_t)notify_count * NOTIFY_THROUGHPUT_PAYLOAD * 8) / actual_secs), notify_count);
|
||||||
|
printf("\n*********************************\n");
|
||||||
|
ESP_LOGI(tag, " Notification test complete (%d sec elapsed)", actual_secs);
|
||||||
|
}
|
||||||
|
}
|
||||||
notify_test_time = 0;
|
notify_test_time = 0;
|
||||||
notify_count = 0;
|
notify_count = 0;
|
||||||
|
notify_time = 0;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -355,6 +368,12 @@ gatts_gap_event(struct ble_gap_event *event, void *arg)
|
|||||||
case BLE_GAP_EVENT_DISCONNECT:
|
case BLE_GAP_EVENT_DISCONNECT:
|
||||||
ESP_LOGI(tag, "disconnect; reason = %d", event->disconnect.reason);
|
ESP_LOGI(tag, "disconnect; reason = %d", event->disconnect.reason);
|
||||||
|
|
||||||
|
/* Stop notification task loop cleanly */
|
||||||
|
notify_state = false;
|
||||||
|
if (notify_task_handle) {
|
||||||
|
xTaskNotifyGive(notify_task_handle);
|
||||||
|
}
|
||||||
|
|
||||||
/* Connection terminated; resume advertising */
|
/* Connection terminated; resume advertising */
|
||||||
#if CONFIG_EXAMPLE_EXTENDED_ADV
|
#if CONFIG_EXAMPLE_EXTENDED_ADV
|
||||||
ble_gap_ext_adv_stop(0);
|
ble_gap_ext_adv_stop(0);
|
||||||
@@ -375,11 +394,6 @@ gatts_gap_event(struct ble_gap_event *event, void *arg)
|
|||||||
|
|
||||||
case BLE_GAP_EVENT_ADV_COMPLETE:
|
case BLE_GAP_EVENT_ADV_COMPLETE:
|
||||||
ESP_LOGI(tag, "adv complete ");
|
ESP_LOGI(tag, "adv complete ");
|
||||||
#if CONFIG_EXAMPLE_EXTENDED_ADV
|
|
||||||
ext_bleprph_advertise();
|
|
||||||
#else
|
|
||||||
gatts_advertise();
|
|
||||||
#endif
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case BLE_GAP_EVENT_SUBSCRIBE:
|
case BLE_GAP_EVENT_SUBSCRIBE:
|
||||||
@@ -388,17 +402,22 @@ gatts_gap_event(struct ble_gap_event *event, void *arg)
|
|||||||
event->subscribe.cur_notify, event->subscribe.attr_handle);
|
event->subscribe.cur_notify, event->subscribe.attr_handle);
|
||||||
if (event->subscribe.attr_handle == notify_handle) {
|
if (event->subscribe.attr_handle == notify_handle) {
|
||||||
notify_state = event->subscribe.cur_notify;
|
notify_state = event->subscribe.cur_notify;
|
||||||
if (arg != NULL) {
|
|
||||||
ESP_LOGI(tag, "notify test time = %d", *(int *)arg);
|
|
||||||
notify_test_time = *((int *)arg);
|
|
||||||
}
|
|
||||||
if (notify_state) {
|
if (notify_state) {
|
||||||
|
/* Always reset test time on new subscription.
|
||||||
|
* The central controls the actual duration by unsubscribing.
|
||||||
|
* Use a large default so the peripheral never stops on its own. */
|
||||||
|
notify_test_time = 3600;
|
||||||
|
ESP_LOGI(tag, "Notifications enabled, test time = %d sec", notify_test_time);
|
||||||
/* Prime the notification pipeline to allow multiple in-flight
|
/* Prime the notification pipeline to allow multiple in-flight
|
||||||
* notifications. This enables the controller to fill connection
|
* notifications. This enables the controller to fill connection
|
||||||
* events with back-to-back PDUs for maximum throughput. */
|
* events with back-to-back PDUs for maximum throughput. */
|
||||||
for (int i = 0; i < NOTIFY_PIPELINE_DEPTH; i++) {
|
for (int i = 0; i < NOTIFY_PIPELINE_DEPTH; i++) {
|
||||||
xSemaphoreGive(notify_sem);
|
if (notify_task_handle) {
|
||||||
|
xTaskNotifyGive(notify_task_handle);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
ESP_LOGI(tag, "Notifications disabled");
|
||||||
}
|
}
|
||||||
} else if (event->subscribe.attr_handle != notify_handle) {
|
} else if (event->subscribe.attr_handle != notify_handle) {
|
||||||
notify_state = event->subscribe.cur_notify;
|
notify_state = event->subscribe.cur_notify;
|
||||||
@@ -407,16 +426,20 @@ gatts_gap_event(struct ble_gap_event *event, void *arg)
|
|||||||
|
|
||||||
case BLE_GAP_EVENT_NOTIFY_TX:
|
case BLE_GAP_EVENT_NOTIFY_TX:
|
||||||
ESP_LOGD(tag, "BLE_GAP_EVENT_NOTIFY_TX success !!");
|
ESP_LOGD(tag, "BLE_GAP_EVENT_NOTIFY_TX success !!");
|
||||||
if ((event->notify_tx.status == 0) ||
|
|
||||||
(event->notify_tx.status == BLE_HS_EDONE)) {
|
/* Always return the pipeline token to prevent the task from blocking permanently,
|
||||||
/* Send new notification i.e. give Semaphore. By definition,
|
* even if the notification failed (e.g., due to disconnect or buffer full). */
|
||||||
* sending new notifications should not be based on successful
|
if (notify_task_handle) {
|
||||||
* notifications sent, but let us adopt this method to avoid too
|
xTaskNotifyGive(notify_task_handle);
|
||||||
* many `BLE_HS_ENOMEM` errors because of continuous transfer of
|
}
|
||||||
* notifications.XXX */
|
|
||||||
xSemaphoreGive(notify_sem);
|
if ((event->notify_tx.status != 0) &&
|
||||||
} else {
|
(event->notify_tx.status != BLE_HS_EDONE)) {
|
||||||
ESP_LOGE(tag, "BLE_GAP_EVENT_NOTIFY_TX notify tx status = %d", event->notify_tx.status);
|
if (event->notify_tx.status == BLE_HS_ENOMEM) {
|
||||||
|
ESP_LOGD(tag, "BLE_GAP_EVENT_NOTIFY_TX flow control (ENOMEM)");
|
||||||
|
} else {
|
||||||
|
ESP_LOGE(tag, "BLE_GAP_EVENT_NOTIFY_TX notify tx status = %d", event->notify_tx.status);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -476,13 +499,8 @@ gatts_on_reset(int reason)
|
|||||||
void gatts_host_task(void *param)
|
void gatts_host_task(void *param)
|
||||||
{
|
{
|
||||||
ESP_LOGI(tag, "BLE Host Task Started");
|
ESP_LOGI(tag, "BLE Host Task Started");
|
||||||
/* Create a counting semaphore for Notification. Can be used to track
|
|
||||||
* successful notification txmission. Optimistically take some big number
|
|
||||||
* for counting Semaphore */
|
|
||||||
notify_sem = xSemaphoreCreateCounting(100, 0);
|
|
||||||
/* This function will return only when nimble_port_stop() is executed */
|
/* This function will return only when nimble_port_stop() is executed */
|
||||||
nimble_port_run();
|
nimble_port_run();
|
||||||
vSemaphoreDelete(notify_sem);
|
|
||||||
nimble_port_freertos_deinit();
|
nimble_port_freertos_deinit();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -511,7 +529,7 @@ void app_main(void)
|
|||||||
ble_hs_cfg.store_status_cb = ble_store_util_status_rr;
|
ble_hs_cfg.store_status_cb = ble_store_util_status_rr;
|
||||||
|
|
||||||
/* Initialize Notify Task */
|
/* Initialize Notify Task */
|
||||||
BaseType_t task_rc = xTaskCreate(notify_task, "notify_task", 4096, NULL, 10, NULL);
|
BaseType_t task_rc = xTaskCreate(notify_task, "notify_task", 4096, NULL, 10, ¬ify_task_handle);
|
||||||
if (task_rc != pdPASS) {
|
if (task_rc != pdPASS) {
|
||||||
ESP_LOGE(tag, "Failed to create notify_task (rc=%d)", task_rc);
|
ESP_LOGE(tag, "Failed to create notify_task (rc=%d)", task_rc);
|
||||||
return ;
|
return ;
|
||||||
|
|||||||
+23
-5
@@ -2,14 +2,32 @@
|
|||||||
# in this example and some example specific misc sizes are increased.
|
# in this example and some example specific misc sizes are increased.
|
||||||
|
|
||||||
#
|
#
|
||||||
# BT config
|
# BT config (universal across all ESP32 variants)
|
||||||
#
|
#
|
||||||
CONFIG_BT_ENABLED=y
|
CONFIG_BT_ENABLED=y
|
||||||
CONFIG_BT_BLUEDROID_ENABLED=n
|
CONFIG_BT_BLUEDROID_ENABLED=n
|
||||||
CONFIG_BT_NIMBLE_ENABLED=y
|
CONFIG_BT_NIMBLE_ENABLED=y
|
||||||
|
|
||||||
|
# === BT 5.0 / 2M PHY support ===
|
||||||
|
# Ignored automatically on classic ESP32 which lacks BT 5.0
|
||||||
|
CONFIG_BT_NIMBLE_EXT_ADV=n
|
||||||
|
CONFIG_BT_NIMBLE_50_FEATURE_SUPPORT=y
|
||||||
|
CONFIG_EXAMPLE_EXTENDED_ADV=n
|
||||||
|
|
||||||
|
# === ATT / GATT ===
|
||||||
CONFIG_BT_NIMBLE_ATT_PREFERRED_MTU=512
|
CONFIG_BT_NIMBLE_ATT_PREFERRED_MTU=512
|
||||||
CONFIG_BT_NIMBLE_TRANSPORT_ACL_FROM_LL_COUNT=20
|
|
||||||
|
# === Memory Pool Tuning ===
|
||||||
|
CONFIG_BT_NIMBLE_MAX_CONNECTIONS=1
|
||||||
|
CONFIG_BT_NIMBLE_MSYS_1_BLOCK_COUNT=80
|
||||||
|
CONFIG_BT_NIMBLE_MSYS_2_BLOCK_SIZE=592
|
||||||
|
CONFIG_BT_NIMBLE_MSYS_2_BLOCK_COUNT=80
|
||||||
|
|
||||||
|
# Increase controller-to-host buffers
|
||||||
|
CONFIG_BT_NIMBLE_TRANSPORT_ACL_FROM_LL_COUNT=40
|
||||||
|
CONFIG_BT_NIMBLE_TRANSPORT_ACL_SIZE=255
|
||||||
CONFIG_BT_NIMBLE_TRANSPORT_EVT_SIZE=255
|
CONFIG_BT_NIMBLE_TRANSPORT_EVT_SIZE=255
|
||||||
CONFIG_BT_NIMBLE_MSYS_1_BLOCK_COUNT=50
|
|
||||||
CONFIG_BT_NIMBLE_LOG_LEVEL=4
|
# Disable NimBLE INFO logging for throughput tests
|
||||||
CONFIG_BT_NIMBLE_LOG_LEVEL_NONE=y
|
CONFIG_BT_NIMBLE_LOG_LEVEL_WARNING=y
|
||||||
|
CONFIG_BT_NIMBLE_LOG_LEVEL=2
|
||||||
|
|||||||
+5
@@ -0,0 +1,5 @@
|
|||||||
|
# === High-Performance Tuning ===
|
||||||
|
# This config is only applied when building for the ESP32-C6 target
|
||||||
|
# It is necessary for the C6 to reach 750+ kbps and 1.3+ Mbps throughput.
|
||||||
|
CONFIG_COMPILER_OPTIMIZATION_PERF=y
|
||||||
|
CONFIG_FREERTOS_HZ=1000
|
||||||
Reference in New Issue
Block a user