fix(nimble): Fix issues found in review for nimble examples

This commit is contained in:
Rahul Tank
2026-07-24 15:19:38 +05:30
parent 0185451ed1
commit 46f3837611
74 changed files with 1298 additions and 612 deletions
@@ -83,6 +83,7 @@ void app_main(void) {
rc = gap_init(); rc = gap_init();
if (rc != 0) { if (rc != 0) {
ESP_LOGE(TAG, "failed to initialize GAP service, error code: %d", rc); ESP_LOGE(TAG, "failed to initialize GAP service, error code: %d", rc);
nimble_port_deinit();
return; return;
} }
#endif #endif
@@ -35,6 +35,9 @@ static void start_advertising(void) {
/* Set device name */ /* Set device name */
name = ble_svc_gap_device_name(); name = ble_svc_gap_device_name();
if (name == NULL) {
name = DEVICE_NAME;
}
adv_fields.name = (uint8_t *)name; adv_fields.name = (uint8_t *)name;
adv_fields.name_len = strlen(name); adv_fields.name_len = strlen(name);
adv_fields.name_is_complete = 1; adv_fields.name_is_complete = 1;
@@ -87,6 +87,7 @@ void app_main(void) {
rc = gap_init(); rc = gap_init();
if (rc != 0) { if (rc != 0) {
ESP_LOGE(TAG, "failed to initialize GAP service, error code: %d", rc); ESP_LOGE(TAG, "failed to initialize GAP service, error code: %d", rc);
nimble_port_deinit();
return; return;
} }
#endif #endif
@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD * SPDX-FileCopyrightText: 2024-2026 Espressif Systems (Shanghai) CO LTD
* *
* SPDX-License-Identifier: Unlicense OR CC0-1.0 * SPDX-License-Identifier: Unlicense OR CC0-1.0
*/ */
@@ -64,6 +64,9 @@ static void start_advertising(void) {
/* Set device name */ /* Set device name */
name = ble_svc_gap_device_name(); name = ble_svc_gap_device_name();
if (name == NULL) {
name = DEVICE_NAME;
}
adv_fields.name = (uint8_t *)name; adv_fields.name = (uint8_t *)name;
adv_fields.name_len = strlen(name); adv_fields.name_len = strlen(name);
adv_fields.name_is_complete = 1; adv_fields.name_is_complete = 1;
@@ -160,26 +163,29 @@ static int gap_event_handler(struct ble_gap_event *event, void *arg) {
print_conn_desc(&desc); print_conn_desc(&desc);
led_on(); led_on();
/* Try to update connection parameters */ /* Try to update connection parameters.
* BT spec requires: supervision_timeout > (1+latency)*itvl/4
* Ensure the timeout satisfies this constraint when latency=3. */
uint16_t min_timeout = (uint16_t)(((1 + 3) * (uint32_t)desc.conn_itvl) / 4 + 1);
uint16_t supervision_timeout = (desc.supervision_timeout > min_timeout)
? desc.supervision_timeout : min_timeout;
struct ble_gap_upd_params params = {.itvl_min = desc.conn_itvl, struct ble_gap_upd_params params = {.itvl_min = desc.conn_itvl,
.itvl_max = desc.conn_itvl, .itvl_max = desc.conn_itvl,
.latency = 3, .latency = 3,
.supervision_timeout = .supervision_timeout = supervision_timeout};
desc.supervision_timeout};
rc = ble_gap_update_params(event->connect.conn_handle, &params); rc = ble_gap_update_params(event->connect.conn_handle, &params);
if (rc != 0) { if (rc != 0) {
ESP_LOGE( ESP_LOGE(
TAG, TAG,
"failed to update connection parameters, error code: %d", "failed to update connection parameters, error code: %d",
rc); rc);
return rc;
} }
} }
/* Connection failed, restart advertising */ /* Connection failed, restart advertising */
else { else {
start_advertising(); start_advertising();
} }
return rc; return 0;
/* Disconnect event */ /* Disconnect event */
case BLE_GAP_EVENT_DISCONNECT: case BLE_GAP_EVENT_DISCONNECT:
@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD * SPDX-FileCopyrightText: 2024-2026 Espressif Systems (Shanghai) CO LTD
* *
* SPDX-License-Identifier: Unlicense OR CC0-1.0 * SPDX-License-Identifier: Unlicense OR CC0-1.0
*/ */
@@ -54,7 +54,7 @@ static void nimble_host_task(void *param) {
nimble_port_run(); nimble_port_run();
/* Clean up at exit */ /* Clean up at exit */
vTaskDelete(NULL); nimble_port_freertos_deinit();
} }
static void heart_rate_task(void *param) { static void heart_rate_task(void *param) {
@@ -114,6 +114,7 @@ void app_main(void) {
rc = gap_init(); rc = gap_init();
if (rc != 0) { if (rc != 0) {
ESP_LOGE(TAG, "failed to initialize GAP service, error code: %d", rc); ESP_LOGE(TAG, "failed to initialize GAP service, error code: %d", rc);
nimble_port_deinit();
return; return;
} }
#endif #endif
@@ -122,6 +123,7 @@ void app_main(void) {
rc = gatt_svc_init(); rc = gatt_svc_init();
if (rc != 0) { if (rc != 0) {
ESP_LOGE(TAG, "failed to initialize GATT server, error code: %d", rc); ESP_LOGE(TAG, "failed to initialize GATT server, error code: %d", rc);
nimble_port_deinit();
return; return;
} }
@@ -129,12 +131,7 @@ void app_main(void) {
nimble_host_config_init(); nimble_host_config_init();
/* Start NimBLE host task thread and return */ /* Start NimBLE host task thread and return */
rc = xTaskCreate(nimble_host_task, "NimBLE Host", 4 * 1024, NULL, nimble_port_freertos_init(nimble_host_task);
5, NULL);
if (rc != pdPASS) {
ESP_LOGE(TAG, "failed to create NimBLE host task");
return;
}
rc = xTaskCreate(heart_rate_task, "Heart Rate", 4 * 1024, NULL, 5, NULL); rc = xTaskCreate(heart_rate_task, "Heart Rate", 4 * 1024, NULL, 5, NULL);
if (rc != pdPASS) { if (rc != pdPASS) {
@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD * SPDX-FileCopyrightText: 2024-2026 Espressif Systems (Shanghai) CO LTD
* *
* SPDX-License-Identifier: Unlicense OR CC0-1.0 * SPDX-License-Identifier: Unlicense OR CC0-1.0
*/ */
@@ -64,6 +64,9 @@ static void start_advertising(void) {
/* Set device name */ /* Set device name */
name = ble_svc_gap_device_name(); name = ble_svc_gap_device_name();
if (name == NULL) {
name = DEVICE_NAME;
}
adv_fields.name = (uint8_t *)name; adv_fields.name = (uint8_t *)name;
adv_fields.name_len = strlen(name); adv_fields.name_len = strlen(name);
adv_fields.name_is_complete = 1; adv_fields.name_is_complete = 1;
@@ -159,26 +162,29 @@ static int gap_event_handler(struct ble_gap_event *event, void *arg) {
/* Print connection descriptor */ /* Print connection descriptor */
print_conn_desc(&desc); print_conn_desc(&desc);
/* Try to update connection parameters */ /* Try to update connection parameters.
* BT spec requires: supervision_timeout > (1+latency)*itvl/4
* Ensure the timeout satisfies this constraint when latency=3. */
uint16_t min_timeout = (uint16_t)(((1 + 3) * (uint32_t)desc.conn_itvl) / 4 + 1);
uint16_t supervision_timeout = (desc.supervision_timeout > min_timeout)
? desc.supervision_timeout : min_timeout;
struct ble_gap_upd_params params = {.itvl_min = desc.conn_itvl, struct ble_gap_upd_params params = {.itvl_min = desc.conn_itvl,
.itvl_max = desc.conn_itvl, .itvl_max = desc.conn_itvl,
.latency = 3, .latency = 3,
.supervision_timeout = .supervision_timeout = supervision_timeout};
desc.supervision_timeout};
rc = ble_gap_update_params(event->connect.conn_handle, &params); rc = ble_gap_update_params(event->connect.conn_handle, &params);
if (rc != 0) { if (rc != 0) {
ESP_LOGE( ESP_LOGE(
TAG, TAG,
"failed to update connection parameters, error code: %d", "failed to update connection parameters, error code: %d",
rc); rc);
return rc;
} }
} }
/* Connection failed, restart advertising */ /* Connection failed, restart advertising */
else { else {
start_advertising(); start_advertising();
} }
return rc; return 0;
/* Disconnect event */ /* Disconnect event */
case BLE_GAP_EVENT_DISCONNECT: case BLE_GAP_EVENT_DISCONNECT:
@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD * SPDX-FileCopyrightText: 2024-2026 Espressif Systems (Shanghai) CO LTD
* *
* SPDX-License-Identifier: Unlicense OR CC0-1.0 * SPDX-License-Identifier: Unlicense OR CC0-1.0
*/ */
@@ -164,9 +164,11 @@ error:
/* Public functions */ /* Public functions */
void send_heart_rate_indication(void) { void send_heart_rate_indication(void) {
if (heart_rate_ind_status && heart_rate_chr_conn_handle_inited) { if (heart_rate_ind_status && heart_rate_chr_conn_handle_inited) {
ble_gatts_indicate(heart_rate_chr_conn_handle, int rc = ble_gatts_indicate(heart_rate_chr_conn_handle,
heart_rate_chr_val_handle); heart_rate_chr_val_handle);
ESP_LOGI(TAG, "heart rate indication sent!"); if (rc != 0) {
ESP_LOGE(TAG, "failed to send heart rate indication, error code: %d", rc);
}
} }
} }
@@ -230,9 +232,13 @@ void gatt_svr_subscribe_cb(struct ble_gap_event *event) {
/* Check attribute handle */ /* Check attribute handle */
if (event->subscribe.attr_handle == heart_rate_chr_val_handle) { if (event->subscribe.attr_handle == heart_rate_chr_val_handle) {
if (event->subscribe.conn_handle == BLE_HS_CONN_HANDLE_NONE) {
return;
}
/* Update heart rate subscription status */ /* Update heart rate subscription status */
heart_rate_chr_conn_handle = event->subscribe.conn_handle; heart_rate_chr_conn_handle = event->subscribe.conn_handle;
heart_rate_chr_conn_handle_inited = true; heart_rate_chr_conn_handle_inited = event->subscribe.cur_indicate;
heart_rate_ind_status = event->subscribe.cur_indicate; heart_rate_ind_status = event->subscribe.cur_indicate;
} }
} }
@@ -15,6 +15,7 @@
#include "host/ble_gap.h" #include "host/ble_gap.h"
/* Public function declarations */ /* Public function declarations */
void gatt_svr_reset_heart_rate_subscription(void);
void send_heart_rate_indication(void); void send_heart_rate_indication(void);
void gatt_svr_register_cb(struct ble_gatt_register_ctxt *ctxt, void *arg); void gatt_svr_register_cb(struct ble_gatt_register_ctxt *ctxt, void *arg);
int gatt_svr_subscribe_cb(struct ble_gap_event *event); int gatt_svr_subscribe_cb(struct ble_gap_event *event);
@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD * SPDX-FileCopyrightText: 2024-2026 Espressif Systems (Shanghai) CO LTD
* *
* SPDX-License-Identifier: Unlicense OR CC0-1.0 * SPDX-License-Identifier: Unlicense OR CC0-1.0
*/ */
@@ -59,7 +59,7 @@ static void nimble_host_task(void *param) {
nimble_port_run(); nimble_port_run();
/* Clean up at exit */ /* Clean up at exit */
vTaskDelete(NULL); nimble_port_freertos_deinit();
} }
static void heart_rate_task(void *param) { static void heart_rate_task(void *param) {
@@ -123,6 +123,7 @@ void app_main(void) {
rc = gap_init(); rc = gap_init();
if (rc != 0) { if (rc != 0) {
ESP_LOGE(TAG, "failed to initialize GAP service, error code: %d", rc); ESP_LOGE(TAG, "failed to initialize GAP service, error code: %d", rc);
nimble_port_deinit();
return; return;
} }
#endif #endif
@@ -131,6 +132,7 @@ void app_main(void) {
rc = gatt_svc_init(); rc = gatt_svc_init();
if (rc != 0) { if (rc != 0) {
ESP_LOGE(TAG, "failed to initialize GATT server, error code: %d", rc); ESP_LOGE(TAG, "failed to initialize GATT server, error code: %d", rc);
nimble_port_deinit();
return; return;
} }
@@ -138,7 +140,12 @@ void app_main(void) {
nimble_host_config_init(); nimble_host_config_init();
/* Start NimBLE host task thread and return */ /* Start NimBLE host task thread and return */
xTaskCreate(nimble_host_task, "NimBLE Host", 4*1024, NULL, 5, NULL); nimble_port_freertos_init(nimble_host_task);
xTaskCreate(heart_rate_task, "Heart Rate", 4*1024, NULL, 5, NULL);
rc = xTaskCreate(heart_rate_task, "Heart Rate", 4 * 1024, NULL, 5, NULL);
if (rc != pdPASS) {
ESP_LOGE(TAG, "failed to create heart rate task");
return;
}
return; return;
} }
@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD * SPDX-FileCopyrightText: 2024-2026 Espressif Systems (Shanghai) CO LTD
* *
* SPDX-License-Identifier: Unlicense OR CC0-1.0 * SPDX-License-Identifier: Unlicense OR CC0-1.0
*/ */
@@ -12,7 +12,6 @@
inline static void format_addr(char *addr_str, uint8_t addr[]); inline static void format_addr(char *addr_str, uint8_t addr[]);
static void print_conn_desc(struct ble_gap_conn_desc *desc); static void print_conn_desc(struct ble_gap_conn_desc *desc);
static void start_advertising(void); static void start_advertising(void);
static void set_random_addr(void);
static int gap_event_handler(struct ble_gap_event *event, void *arg); static int gap_event_handler(struct ble_gap_event *event, void *arg);
/* Private variables */ /* Private variables */
@@ -52,20 +51,6 @@ static void print_conn_desc(struct ble_gap_conn_desc *desc) {
desc->sec_state.bonded); desc->sec_state.bonded);
} }
static void set_random_addr(void) {
/* Local variables */
int rc = 0;
ble_addr_t addr;
/* Generate new non-resolvable private address */
rc = ble_hs_id_gen_rnd(0, &addr);
assert(rc == 0);
/* Set address */
rc = ble_hs_id_set_rnd(addr.val);
assert(rc == 0);
}
static void start_advertising(void) { static void start_advertising(void) {
/* Local variables */ /* Local variables */
int rc = 0; int rc = 0;
@@ -79,6 +64,9 @@ static void start_advertising(void) {
/* Set device name */ /* Set device name */
name = ble_svc_gap_device_name(); name = ble_svc_gap_device_name();
if (name == NULL) {
name = DEVICE_NAME;
}
adv_fields.name = (uint8_t *)name; adv_fields.name = (uint8_t *)name;
adv_fields.name_len = strlen(name); adv_fields.name_len = strlen(name);
adv_fields.name_is_complete = 1; adv_fields.name_is_complete = 1;
@@ -201,6 +189,9 @@ static int gap_event_handler(struct ble_gap_event *event, void *arg) {
ESP_LOGI(TAG, "disconnected from peer; reason=%d", ESP_LOGI(TAG, "disconnected from peer; reason=%d",
event->disconnect.reason); event->disconnect.reason);
/* Reset heart rate subscription state */
gatt_svr_reset_heart_rate_subscription();
/* Restart advertising */ /* Restart advertising */
start_advertising(); start_advertising();
return rc; return rc;
@@ -324,8 +315,9 @@ void adv_init(void) {
int rc = 0; int rc = 0;
char addr_str[18] = {0}; char addr_str[18] = {0};
/* Make sure we have proper BT identity address set */ /* Make sure we have proper BT identity address set.
set_random_addr(); * ble_hs_util_ensure_addr(1) safely generates a random address only if
* one is not already set, avoiding conflicts on re-sync events. */
rc = ble_hs_util_ensure_addr(1); rc = ble_hs_util_ensure_addr(1);
if (rc != 0) { if (rc != 0) {
ESP_LOGE(TAG, "device does not have any available bt address!"); ESP_LOGE(TAG, "device does not have any available bt address!");
@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD * SPDX-FileCopyrightText: 2024-2026 Espressif Systems (Shanghai) CO LTD
* *
* SPDX-License-Identifier: Unlicense OR CC0-1.0 * SPDX-License-Identifier: Unlicense OR CC0-1.0
*/ */
@@ -24,7 +24,7 @@ static uint8_t heart_rate_chr_val[2] = {0};
static uint16_t heart_rate_chr_val_handle; static uint16_t heart_rate_chr_val_handle;
static const ble_uuid16_t heart_rate_chr_uuid = BLE_UUID16_INIT(0x2A37); static const ble_uuid16_t heart_rate_chr_uuid = BLE_UUID16_INIT(0x2A37);
static uint16_t heart_rate_chr_conn_handle = 0; static uint16_t heart_rate_chr_conn_handle = BLE_HS_CONN_HANDLE_NONE;
static bool heart_rate_chr_conn_handle_inited = false; static bool heart_rate_chr_conn_handle_inited = false;
static bool heart_rate_ind_status = false; static bool heart_rate_ind_status = false;
@@ -165,6 +165,12 @@ error:
} }
/* Public functions */ /* Public functions */
void gatt_svr_reset_heart_rate_subscription(void) {
heart_rate_chr_conn_handle_inited = false;
heart_rate_ind_status = false;
heart_rate_chr_conn_handle = BLE_HS_CONN_HANDLE_NONE;
}
void send_heart_rate_indication(void) { void send_heart_rate_indication(void) {
/* Check if connection handle is initialized */ /* Check if connection handle is initialized */
if (!heart_rate_chr_conn_handle_inited) { if (!heart_rate_chr_conn_handle_inited) {
@@ -230,10 +236,19 @@ void gatt_svr_register_cb(struct ble_gatt_register_ctxt *ctxt, void *arg) {
int gatt_svr_subscribe_cb(struct ble_gap_event *event) { int gatt_svr_subscribe_cb(struct ble_gap_event *event) {
/* Check attribute handle */ /* Check attribute handle */
if (event->subscribe.attr_handle == heart_rate_chr_val_handle) { if (event->subscribe.attr_handle == heart_rate_chr_val_handle) {
if (event->subscribe.conn_handle == BLE_HS_CONN_HANDLE_NONE) {
return 0;
}
if (!event->subscribe.cur_indicate) {
gatt_svr_reset_heart_rate_subscription();
return 0;
}
/* Update heart rate subscription status */ /* Update heart rate subscription status */
heart_rate_chr_conn_handle = event->subscribe.conn_handle; heart_rate_chr_conn_handle = event->subscribe.conn_handle;
heart_rate_chr_conn_handle_inited = true; heart_rate_chr_conn_handle_inited = true;
heart_rate_ind_status = event->subscribe.cur_indicate; heart_rate_ind_status = true;
/* Check security status */ /* Check security status */
if (!is_connection_encrypted(event->subscribe.conn_handle)) { if (!is_connection_encrypted(event->subscribe.conn_handle)) {
@@ -607,7 +607,9 @@ static void ble_ams_advertise(void)
{ {
struct ble_gap_adv_params adv_params; struct ble_gap_adv_params adv_params;
struct ble_hs_adv_fields fields; struct ble_hs_adv_fields fields;
#if CONFIG_BT_NIMBLE_GAP_SERVICE
const char *name; const char *name;
#endif
int rc; int rc;
memset(&fields, 0, sizeof(fields)); memset(&fields, 0, sizeof(fields));
@@ -615,10 +617,12 @@ static void ble_ams_advertise(void)
fields.tx_pwr_lvl_is_present = 1; fields.tx_pwr_lvl_is_present = 1;
fields.tx_pwr_lvl = BLE_HS_ADV_TX_PWR_LVL_AUTO; fields.tx_pwr_lvl = BLE_HS_ADV_TX_PWR_LVL_AUTO;
#if CONFIG_BT_NIMBLE_GAP_SERVICE
name = ble_svc_gap_device_name(); name = ble_svc_gap_device_name();
fields.name = (uint8_t *)name; fields.name = (uint8_t *)name;
fields.name_len = strlen(name); fields.name_len = strlen(name);
fields.name_is_complete = 1; fields.name_is_complete = 1;
#endif
fields.appearance = BLE_AMS_APPEARANCE; fields.appearance = BLE_AMS_APPEARANCE;
fields.appearance_is_present = 1; fields.appearance_is_present = 1;
@@ -885,6 +889,7 @@ static void ble_ams_host_task(void *param)
void app_main(void) void app_main(void)
{ {
int rc;
esp_err_t ret = nvs_flash_init(); esp_err_t ret = nvs_flash_init();
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) { if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
ESP_ERROR_CHECK(nvs_flash_erase()); ESP_ERROR_CHECK(nvs_flash_erase());
@@ -907,10 +912,12 @@ void app_main(void)
ble_hs_cfg.sm_their_key_dist |= BLE_SM_PAIR_KEY_DIST_ENC; ble_hs_cfg.sm_their_key_dist |= BLE_SM_PAIR_KEY_DIST_ENC;
ble_hs_cfg.sm_sc = 0; ble_hs_cfg.sm_sc = 0;
int rc = ble_svc_gap_device_name_set("nimble-ams"); #if CONFIG_BT_NIMBLE_GAP_SERVICE
rc = ble_svc_gap_device_name_set("nimble-ams");
assert(rc == 0); assert(rc == 0);
rc = ble_svc_gap_device_appearance_set(BLE_AMS_APPEARANCE); rc = ble_svc_gap_device_appearance_set(BLE_AMS_APPEARANCE);
assert(rc == 0); assert(rc == 0);
#endif
ble_store_config_init(); ble_store_config_init();
@@ -113,7 +113,7 @@ void ble_receive_apple_notification_source(uint8_t *message, uint16_t message_le
uint8_t CategoryID = message[2]; uint8_t CategoryID = message[2];
char *Cidstr = CategoryID_to_String(CategoryID); char *Cidstr = CategoryID_to_String(CategoryID);
uint8_t CategoryCount = message[3]; uint8_t CategoryCount = message[3];
uint32_t NotificationUID = (message[4]) | (message[5]<< 8) | (message[6]<< 16) | (message[7] << 24); uint32_t NotificationUID = ((uint32_t)message[4]) | ((uint32_t)message[5] << 8) | ((uint32_t)message[6] << 16) | ((uint32_t)message[7] << 24);
ESP_LOGI(NimBLE_ANCS_TAG, "EventID:%s EventFlags:0x%x CategoryID:%s CategoryCount:%d NotificationUID:%" PRIu32, EventIDS, EventFlags, Cidstr, CategoryCount, NotificationUID); ESP_LOGI(NimBLE_ANCS_TAG, "EventID:%s EventFlags:0x%x CategoryID:%s CategoryCount:%d NotificationUID:%" PRIu32, EventIDS, EventFlags, Cidstr, CategoryCount, NotificationUID);
} }
@@ -131,14 +131,18 @@ void ble_receive_apple_data_source(uint8_t *message, uint16_t message_len)
message_len); message_len);
return; return;
} }
uint32_t NotificationUID = (message[1]) | (message[2]<< 8) | (message[3]<< 16) | (message[4] << 24); uint32_t NotificationUID = ((uint32_t)message[1]) | ((uint32_t)message[2] << 8) | ((uint32_t)message[3] << 16) | ((uint32_t)message[4] << 24);
uint32_t remain_attr_len = message_len - 5; uint32_t remain_attr_len = message_len - 5;
uint8_t *attrs = &message[5]; uint8_t *attrs = &message[5];
ESP_LOGI(NimBLE_ANCS_TAG, "recevice Notification Attributes response Command_id %d NotificationUID %" PRIu32, Command_id, NotificationUID); ESP_LOGI(NimBLE_ANCS_TAG, "recevice Notification Attributes response Command_id %d NotificationUID %" PRIu32, Command_id, NotificationUID);
while(remain_attr_len > 0) { while(remain_attr_len > 0) {
if (remain_attr_len < 3) {
ESP_LOGE(NimBLE_ANCS_TAG, "incomplete attribute header");
break;
}
uint8_t AttributeID = attrs[0]; uint8_t AttributeID = attrs[0];
uint16_t len = attrs[1] | (attrs[2] << 8); uint16_t len = attrs[1] | (attrs[2] << 8);
if(len > (remain_attr_len -3)) { if(len > (remain_attr_len - 3)) {
ESP_LOGE(NimBLE_ANCS_TAG, "data error"); ESP_LOGE(NimBLE_ANCS_TAG, "data error");
break; break;
} }
+22 -2
View File
@@ -344,7 +344,7 @@ ext_ble_ancs_advertise(void)
params.connectable = 1; params.connectable = 1;
/* advertise using configured addr */ /* advertise using configured addr */
params.own_addr_type = BLE_OWN_ADDR_PUBLIC; params.own_addr_type = own_addr_type;
params.primary_phy = BLE_HCI_LE_PHY_1M; params.primary_phy = BLE_HCI_LE_PHY_1M;
params.secondary_phy = BLE_HCI_LE_PHY_2M; params.secondary_phy = BLE_HCI_LE_PHY_2M;
params.tx_power = 127; params.tx_power = 127;
@@ -413,11 +413,15 @@ ble_ancs_advertise(void)
fields.tx_pwr_lvl_is_present = 1; fields.tx_pwr_lvl_is_present = 1;
fields.tx_pwr_lvl = BLE_HS_ADV_TX_PWR_LVL_AUTO; fields.tx_pwr_lvl = BLE_HS_ADV_TX_PWR_LVL_AUTO;
#if CONFIG_BT_NIMBLE_GAP_SERVICE
name = ble_svc_gap_device_name(); name = ble_svc_gap_device_name();
fields.name = (uint8_t *)name; fields.name = (uint8_t *)name;
fields.name_len = strlen(name); fields.name_len = strlen(name);
fields.name_is_complete = 1; fields.name_is_complete = 1;
#endif
static const ble_uuid16_t adv_uuids16[] = { BLE_UUID16_INIT(0x1811) };
fields.uuids16 = adv_uuids16;
fields.num_uuids16 = 1; fields.num_uuids16 = 1;
fields.uuids16_is_complete = 1; fields.uuids16_is_complete = 1;
@@ -485,6 +489,7 @@ ble_ancs_gap_event(struct ble_gap_event *event, void *arg)
#else #else
ble_ancs_advertise(); ble_ancs_advertise();
#endif #endif
return 0;
} }
/** Initiate security - It will perform /** Initiate security - It will perform
@@ -544,7 +549,11 @@ ble_ancs_gap_event(struct ble_gap_event *event, void *arg)
assert(rc == 0); assert(rc == 0);
ble_ancs_print_conn_desc(&desc); ble_ancs_print_conn_desc(&desc);
MODLOG_DFLT(INFO, "\n"); MODLOG_DFLT(INFO, "\n");
rc = ble_gattc_disc_svc_by_uuid(event->connect.conn_handle, &APPLE_NC_UUID.u, if (event->enc_change.status != 0) {
MODLOG_DFLT(ERROR, "encryption failed; status=%d\n", event->enc_change.status);
return 0;
}
rc = ble_gattc_disc_svc_by_uuid(event->enc_change.conn_handle, &APPLE_NC_UUID.u,
ancs_service_discovered_cb, NULL); ancs_service_discovered_cb, NULL);
if (rc != 0) { if (rc != 0) {
return rc; return rc;
@@ -563,6 +572,10 @@ ble_ancs_gap_event(struct ble_gap_event *event, void *arg)
case BLE_GAP_EVENT_NOTIFY_RX: case BLE_GAP_EVENT_NOTIFY_RX:
/* Peer sent us a notification or indication. */ /* Peer sent us a notification or indication. */
if (event->notify_rx.attr_handle == notification_source_handle) { if (event->notify_rx.attr_handle == notification_source_handle) {
if (event->notify_rx.om == NULL || event->notify_rx.om->om_len < 8) {
MODLOG_DFLT(ERROR, "NOTIFY_RX: short or NULL notification source packet\n");
return 0;
}
ble_receive_apple_notification_source(event->notify_rx.om->om_data, event->notify_rx.om->om_len); ble_receive_apple_notification_source(event->notify_rx.om->om_data, event->notify_rx.om->om_len);
uint8_t *notificationUID = &event->notify_rx.om->om_data[4]; uint8_t *notificationUID = &event->notify_rx.om->om_data[4];
if (event->notify_rx.om->om_data[0] == EventIDNotificationAdded && if (event->notify_rx.om->om_data[0] == EventIDNotificationAdded &&
@@ -773,6 +786,13 @@ app_main(void)
/* XXX Need to have template for store */ /* XXX Need to have template for store */
ble_store_config_init(); ble_store_config_init();
ret = esp_timer_create(&periodic_timer_args, &periodic_timer);
if (ret != ESP_OK) {
ESP_LOGE(tag, "Failed to create periodic timer: %d", ret);
nimble_port_deinit();
return;
}
nimble_port_freertos_init(ble_ancs_host_task); nimble_port_freertos_init(ble_ancs_host_task);
} }
@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD * SPDX-FileCopyrightText: 2021-2026 Espressif Systems (Shanghai) CO LTD
* SPDX-License-Identifier: Unlicense OR CC0-1.0 * SPDX-License-Identifier: Unlicense OR CC0-1.0
*/ */
@@ -79,6 +79,10 @@ int blecent_on_subscribe(uint16_t conn_handle,
param.cb_arg=NULL; param.cb_arg=NULL;
ble_cs_initiator_procedure_start(&param); ble_cs_initiator_procedure_start(&param);
} }
} else {
MODLOG_DFLT(ERROR, "Subscription failed; status=%d conn_handle=%d\n",
error->status, conn_handle);
ble_gap_terminate(conn_handle, BLE_ERR_REM_USER_CONN_TERM);
} }
return 0; return 0;
@@ -180,6 +184,7 @@ blecent_on_custom_read(uint16_t conn_handle,
rc = ble_chan_ras_subsribe_by_uuid(BLE_UUID16_DECLARE(BLE_UUID_RAS_REALTIME_RD_VAL), peer, conn_handle); rc = ble_chan_ras_subsribe_by_uuid(BLE_UUID16_DECLARE(BLE_UUID_RAS_REALTIME_RD_VAL), peer, conn_handle);
if (rc != 0) { if (rc != 0) {
MODLOG_DFLT(ERROR, "Error: Peer doesn't support the RAS on demand raging data or fail to subscribe \n"); MODLOG_DFLT(ERROR, "Error: Peer doesn't support the RAS on demand raging data or fail to subscribe \n");
ble_gap_terminate(peer->conn_handle, BLE_ERR_REM_USER_CONN_TERM);
return -1; return -1;
} else { } else {
MODLOG_DFLT(INFO, "Subscribed to the RAS Realtime Ranging Data characteristic\n"); MODLOG_DFLT(INFO, "Subscribed to the RAS Realtime Ranging Data characteristic\n");
@@ -189,6 +194,7 @@ blecent_on_custom_read(uint16_t conn_handle,
rc = ble_chan_ras_subsribe_by_uuid(BLE_UUID16_DECLARE(BLE_UUID_RAS_ONDEMAND_RD_VAL), peer, conn_handle); rc = ble_chan_ras_subsribe_by_uuid(BLE_UUID16_DECLARE(BLE_UUID_RAS_ONDEMAND_RD_VAL), peer, conn_handle);
if (rc != 0) { if (rc != 0) {
MODLOG_DFLT(ERROR, "Error: Peer doesn't support the RAS on demand ranging data or fail to subscribe \n"); MODLOG_DFLT(ERROR, "Error: Peer doesn't support the RAS on demand ranging data or fail to subscribe \n");
ble_gap_terminate(peer->conn_handle, BLE_ERR_REM_USER_CONN_TERM);
return -1; return -1;
} else { } else {
MODLOG_DFLT(INFO, "Subscribed to the RAS On Demand Ranging Data characteristic\n"); MODLOG_DFLT(INFO, "Subscribed to the RAS On Demand Ranging Data characteristic\n");
@@ -197,7 +203,11 @@ blecent_on_custom_read(uint16_t conn_handle,
ble_chan_ras_subscribe(peer, conn_handle); ble_chan_ras_subscribe(peer, conn_handle);
} else if (error->status == BLE_ATT_ERR_INSUFFICIENT_AUTHEN) { } else if (error->status == BLE_ATT_ERR_INSUFFICIENT_AUTHEN) {
MODLOG_DFLT(INFO, "Error: Insufficient authentication to read the characteristic\n"); MODLOG_DFLT(INFO, "Error: Insufficient authentication to read the characteristic\n");
ble_gap_terminate(peer->conn_handle, BLE_ERR_REM_USER_CONN_TERM);
return -1; return -1;
} else {
MODLOG_DFLT(ERROR, "Error: Failed to read RAS Features; status=%d\n", error->status);
ble_gap_terminate(peer->conn_handle, BLE_ERR_REM_USER_CONN_TERM);
} }
return 0; return 0;
} }
@@ -279,6 +289,7 @@ blecent_on_disc_complete(const struct peer *peer, int status, void *arg)
blecent_on_custom_read, (void *)peer); blecent_on_custom_read, (void *)peer);
if (rc != 0) { if (rc != 0) {
MODLOG_DFLT(ERROR, "2 Error: Failed to read the custom subscribable characteristic; ""rc=%d\n", rc); MODLOG_DFLT(ERROR, "2 Error: Failed to read the custom subscribable characteristic; ""rc=%d\n", rc);
ble_gap_terminate(peer->conn_handle, BLE_ERR_REM_USER_CONN_TERM);
} }
} }
@@ -289,7 +300,7 @@ static void
blecent_scan(void) blecent_scan(void)
{ {
uint8_t own_addr_type; uint8_t own_addr_type;
struct ble_gap_disc_params disc_params; struct ble_gap_disc_params disc_params = {0};
int rc; int rc;
/* Figure out address to use while advertising (no privacy for now) */ /* Figure out address to use while advertising (no privacy for now) */
@@ -351,15 +362,17 @@ blecent_connect_if_interesting(void *disc)
adv_data_len = d->length_data; adv_data_len = d->length_data;
#endif #endif
for (int i = 1; i < adv_data_len - 1; i++) { struct ble_hs_adv_fields adv_fields;
if (adv_data[i-1] == 0x03 && adv_data[i] == 0x5b && adv_data[i+1] == 0x18) { if (ble_hs_adv_parse_fields(&adv_fields, adv_data, adv_data_len) == 0) {
found = 1; for (int i = 0; i < adv_fields.num_uuids16; i++) {
MODLOG_DFLT(DEBUG, "Found 0x5b18 at index %d\n", i); if (adv_fields.uuids16[i].value == BLE_UUID_RANGING_SERVICE_VAL) {
break; found = 1;
break;
}
} }
} }
if (!found) { if (!found) {
MODLOG_DFLT(DEBUG, "0x5b18 not found in adv_data, skipping connect.\n"); MODLOG_DFLT(DEBUG, "Ranging Service UUID not found in adv_data, skipping connect.\n");
return; return;
} }
#if !(MYNEWT_VAL(BLE_HOST_ALLOW_CONNECT_WITH_SCAN)) #if !(MYNEWT_VAL(BLE_HOST_ALLOW_CONNECT_WITH_SCAN))
@@ -374,12 +387,18 @@ blecent_connect_if_interesting(void *disc)
rc = ble_hs_id_infer_auto(0, &own_addr_type); rc = ble_hs_id_infer_auto(0, &own_addr_type);
if (rc != 0) { if (rc != 0) {
MODLOG_DFLT(ERROR, "error determining address type; rc=%d\n", rc); MODLOG_DFLT(ERROR, "error determining address type; rc=%d\n", rc);
#if !(MYNEWT_VAL(BLE_HOST_ALLOW_CONNECT_WITH_SCAN))
blecent_scan();
#endif
return; return;
} }
rc = ble_gap_connect(own_addr_type, addr, 30000, NULL, rc = ble_gap_connect(own_addr_type, addr, 30000, NULL,
blecent_gap_event, NULL); blecent_gap_event, NULL);
if (rc != 0) { if (rc != 0) {
MODLOG_DFLT(ERROR, "Connection failed.\n"); MODLOG_DFLT(ERROR, "Connection failed.\n");
#if !(MYNEWT_VAL(BLE_HOST_ALLOW_CONNECT_WITH_SCAN))
blecent_scan();
#endif
return; return;
} }
} }
@@ -577,18 +596,19 @@ blecent_gap_event(struct ble_gap_event *event, void *arg)
rc = ble_gattc_write_no_rsp_flat(event->notify_rx.conn_handle, ble_svc_ras_cp_val_handle, &value, sizeof value); rc = ble_gattc_write_no_rsp_flat(event->notify_rx.conn_handle, ble_svc_ras_cp_val_handle, &value, sizeof value);
} else if( value[0] == RASCP_RSP_OPCODE_RSP_CODE) { } else if( value[0] == RASCP_RSP_OPCODE_RSP_CODE) {
MODLOG_DFLT(INFO, "Sucssefully completed the Ranging procedure\n"); MODLOG_DFLT(INFO, "Sucssefully completed the Ranging procedure\n");
vTaskDelay(1000 / portTICK_PERIOD_MS);
} }
} }
else if(ble_svc_ras_rd_val_handle == event->notify_rx.attr_handle){ else if(ble_svc_ras_rd_val_handle == event->notify_rx.attr_handle){
MODLOG_DFLT(INFO, "Received Ranging Data Ready Indication\n"); MODLOG_DFLT(INFO, "Received Ranging Data Ready Indication\n");
if (OS_MBUF_PKTLEN(event->notify_rx.om) < sizeof(uint16_t)) {
MODLOG_DFLT(ERROR, "Error: Invalid Ranging Data Ready Indication length\n");
return 0;
}
uint16_t ranging_counter; uint16_t ranging_counter;
os_mbuf_copydata(event->notify_rx.om, 0, sizeof(uint16_t), &ranging_counter); os_mbuf_copydata(event->notify_rx.om, 0, sizeof(uint16_t), &ranging_counter);
most_recent_peer_ranging_counter=ranging_counter; most_recent_peer_ranging_counter = ranging_counter;
most_recent_local_ranging_counter= ranging_counter;
if (most_recent_peer_ranging_counter!=most_recent_local_ranging_counter) { if (most_recent_peer_ranging_counter != most_recent_local_ranging_counter) {
MODLOG_DFLT(INFO, "Ranging counter mismatch : %" PRId32 " , %" PRId32, MODLOG_DFLT(INFO, "Ranging counter mismatch : %" PRId32 " , %" PRId32,
most_recent_peer_ranging_counter,most_recent_local_ranging_counter); most_recent_peer_ranging_counter,most_recent_local_ranging_counter);
return 0; return 0;
@@ -71,13 +71,17 @@ void print_cs_event(const struct ble_cs_event *event)
MODLOG_DFLT(INFO, "subev_result.num_antenna_paths = %u\n", event->subev_result.num_antenna_paths); MODLOG_DFLT(INFO, "subev_result.num_antenna_paths = %u\n", event->subev_result.num_antenna_paths);
MODLOG_DFLT(INFO, "subev_result.num_steps_reported = %u\n", event->subev_result.num_steps_reported); MODLOG_DFLT(INFO, "subev_result.num_steps_reported = %u\n", event->subev_result.num_steps_reported);
for (int i = 0; i < event->subev_result.num_steps_reported; i++) { {
const struct cs_steps_data *step = &event->subev_result.steps[i]; const uint8_t *p = (const uint8_t *)event->subev_result.steps;
MODLOG_DFLT(INFO, "steps[%d]: mode=%u, channel=%u, data_len=%u, data=", i, step->mode, step->channel, step->data_len); for (int i = 0; i < event->subev_result.num_steps_reported; i++) {
for (int j = 0; j < step->data_len; j++) { const struct cs_steps_data *step = (const struct cs_steps_data *)p;
esp_rom_printf("%02x ", step->data[j]); MODLOG_DFLT(INFO, "steps[%d]: mode=%u, channel=%u, data_len=%u, data=", i, step->mode, step->channel, step->data_len);
for (int j = 0; j < step->data_len; j++) {
esp_rom_printf("%02x ", step->data[j]);
}
esp_rom_printf("\n");
p += sizeof(struct cs_steps_data) + step->data_len;
} }
esp_rom_printf("\n");
} }
break; break;
case BLE_CS_EVENT_SUBEVET_RESULT_CONTINUE: case BLE_CS_EVENT_SUBEVET_RESULT_CONTINUE:
@@ -89,13 +93,17 @@ void print_cs_event(const struct ble_cs_event *event)
MODLOG_DFLT(INFO, "subev_result_continue.num_antenna_paths = %u\n", event->subev_result_continue.num_antenna_paths); MODLOG_DFLT(INFO, "subev_result_continue.num_antenna_paths = %u\n", event->subev_result_continue.num_antenna_paths);
MODLOG_DFLT(INFO, "subev_result_continue.num_steps_reported = %u\n", event->subev_result_continue.num_steps_reported); MODLOG_DFLT(INFO, "subev_result_continue.num_steps_reported = %u\n", event->subev_result_continue.num_steps_reported);
for (int i = 0; i < event->subev_result_continue.num_steps_reported; i++) { {
const struct cs_steps_data *step = &event->subev_result_continue.steps[i]; const uint8_t *p = (const uint8_t *)event->subev_result_continue.steps;
MODLOG_DFLT(INFO, "steps[%d]: mode=%u, channel=%u, data_len=%u, data=", i, step->mode, step->channel, step->data_len); for (int i = 0; i < event->subev_result_continue.num_steps_reported; i++) {
for (int j = 0; j < step->data_len; j++) { const struct cs_steps_data *step = (const struct cs_steps_data *)p;
esp_rom_printf("%02x ", step->data[j]); MODLOG_DFLT(INFO, "steps[%d]: mode=%u, channel=%u, data_len=%u, data=", i, step->mode, step->channel, step->data_len);
for (int j = 0; j < step->data_len; j++) {
esp_rom_printf("%02x ", step->data[j]);
}
esp_rom_printf("\n");
p += sizeof(struct cs_steps_data) + step->data_len;
} }
esp_rom_printf("\n");
} }
break; break;
@@ -124,6 +132,7 @@ static int blecs_gap_event(struct ble_cs_event *event, void *arg)
if (idx==1) { if (idx==1) {
most_recent_local_ranging_counter=event->subev_result.procedure_counter; most_recent_local_ranging_counter=event->subev_result.procedure_counter;
} }
ble_gatts_store_ranging_data(ranging_subevent);
MODLOG_DFLT(INFO, "LE CS Subevent Result , status: Partial, procedure counter %d\n", event->subev_result.procedure_counter); MODLOG_DFLT(INFO, "LE CS Subevent Result , status: Partial, procedure counter %d\n", event->subev_result.procedure_counter);
} else { } else {
MODLOG_DFLT(INFO, "LE CS Subevent Result , status: Unknown\n"); MODLOG_DFLT(INFO, "LE CS Subevent Result , status: Unknown\n");
@@ -135,16 +144,19 @@ static int blecs_gap_event(struct ble_cs_event *event, void *arg)
MODLOG_DFLT(INFO, "LE CS Subevent Result Continue , status: Aborted\n"); MODLOG_DFLT(INFO, "LE CS Subevent Result Continue , status: Aborted\n");
} else if ( event->subev_result_continue.procedure_done_status == BLE_HCI_LE_CS_SUBEVENT_DONE_STATUS_COMPLETE) { } else if ( event->subev_result_continue.procedure_done_status == BLE_HCI_LE_CS_SUBEVENT_DONE_STATUS_COMPLETE) {
MODLOG_DFLT(INFO, "LE CS Subevent Result Continue , status: Complete\n"); MODLOG_DFLT(INFO, "LE CS Subevent Result Continue , status: Complete\n");
ranging_subevent.type = BLE_CS_EVENT_SUBEVET_RESULT_CONTINUE;
ranging_subevent.subev_result_continue = event->subev_result_continue;
/* To /* To
* Get total number of CS procedure from CS enable event and then accordigly indicate to most recent ranging counter * Get total number of CS procedure from CS enable event and then accordigly indicate to most recent ranging counter
Currently we are considering only one CS procedure Currently we are considering only one CS procedure
*/ */
ind ++; ind ++;
if (ind==1) { if (ind==1) {
ble_gatts_store_ranging_data(ranging_subevent); struct ble_cs_event continue_event;
continue_event.type = BLE_CS_EVENT_SUBEVET_RESULT_CONTINUE;
continue_event.subev_result_continue = event->subev_result_continue;
ble_gatts_store_ranging_data(continue_event);
ble_gatts_indicate_ranging_data_ready(most_recent_local_ranging_counter); ble_gatts_indicate_ranging_data_ready(most_recent_local_ranging_counter);
idx = 0;
ind = 0;
} }
} }
@@ -226,6 +238,9 @@ ext_bleprph_advertise(void)
/* start advertising */ /* start advertising */
rc = ble_gap_ext_adv_start(instance, 0, 0); rc = ble_gap_ext_adv_start(instance, 0, 0);
if (rc != 0) {
MODLOG_DFLT(ERROR, "error starting extended advertisement; rc=%d\n", rc);
}
} }
#else #else
/** /**
@@ -266,9 +281,10 @@ bleprph_advertise(void)
fields.name_len = strlen(name); fields.name_len = strlen(name);
fields.name_is_complete = 1; fields.name_is_complete = 1;
#endif #endif
fields.uuids16 = (ble_uuid16_t[]) { static const ble_uuid16_t uuids16[] = {
BLE_UUID16_INIT(BLE_UUID_RANGING_SERVICE_VAL) BLE_UUID16_INIT(BLE_UUID_RANGING_SERVICE_VAL)
}; };
fields.uuids16 = uuids16;
fields.num_uuids16 = 1; fields.num_uuids16 = 1;
fields.uuids16_is_complete = 1; fields.uuids16_is_complete = 1;
rc = ble_gap_adv_set_fields(&fields); rc = ble_gap_adv_set_fields(&fields);
@@ -335,6 +351,9 @@ bleprph_gap_event(struct ble_gap_event *event, void *arg)
MODLOG_DFLT(INFO, "disconnect; reason=%d ", event->disconnect.reason); MODLOG_DFLT(INFO, "disconnect; reason=%d ", event->disconnect.reason);
bleprph_print_conn_desc(&event->disconnect.conn); bleprph_print_conn_desc(&event->disconnect.conn);
MODLOG_DFLT(INFO, "\n"); MODLOG_DFLT(INFO, "\n");
idx = 0;
ind = 0;
most_recent_local_ranging_counter = -1;
/* Connection terminated; resume advertising. */ /* Connection terminated; resume advertising. */
#if CONFIG_EXAMPLE_EXTENDED_ADV #if CONFIG_EXAMPLE_EXTENDED_ADV
ext_bleprph_advertise(); ext_bleprph_advertise();
@@ -381,7 +400,6 @@ bleprph_gap_event(struct ble_gap_event *event, void *arg)
event->notify_tx.indication); event->notify_tx.indication);
if (event->notify_tx.status == BLE_HS_EDONE) { if (event->notify_tx.status == BLE_HS_EDONE) {
vTaskDelay(4000 / portTICK_PERIOD_MS);
ble_gatts_indicate_control_point_response(event->notify_tx.attr_handle,most_recent_local_ranging_counter); ble_gatts_indicate_control_point_response(event->notify_tx.attr_handle,most_recent_local_ranging_counter);
} }
return 0; return 0;
@@ -409,6 +427,9 @@ static void
bleprph_on_reset(int reason) bleprph_on_reset(int reason)
{ {
MODLOG_DFLT(ERROR, "Resetting state; reason=%d\n", reason); MODLOG_DFLT(ERROR, "Resetting state; reason=%d\n", reason);
idx = 0;
ind = 0;
most_recent_local_ranging_counter = -1;
} }
static void static void
@@ -54,10 +54,9 @@ static void start_periodic_adv_cte(uint8_t own_addr_type)
assert(rc == 0); assert(rc == 0);
/* Configure advertising data */ /* Configure advertising data */
struct ble_hs_adv_fields adv_fields = { struct ble_hs_adv_fields adv_fields = {0};
.name = (const uint8_t *)"CTE_Periodic_Adv", adv_fields.name = (const uint8_t *)"CTE_Periodic_Adv";
.name_len = strlen((char *)adv_fields.name) adv_fields.name_len = strlen("CTE_Periodic_Adv");
};
struct os_mbuf *data = os_msys_get_pkthdr(BLE_HS_ADV_MAX_FIELD_SZ, 0); struct os_mbuf *data = os_msys_get_pkthdr(BLE_HS_ADV_MAX_FIELD_SZ, 0);
assert(data); assert(data);
@@ -198,7 +197,12 @@ void app_main(void)
ESP_LOGI(TAG, "%s", direction_finding_logo); ESP_LOGI(TAG, "%s", direction_finding_logo);
#if defined(CONFIG_EXAMPLE_ADV_DIRECTION_FINDING_AOD) #if defined(CONFIG_EXAMPLE_ADV_DIRECTION_FINDING_AOD)
ESP_LOGI(TAG, "DIRECTION_FINDING Example Periodic Adv AOD Mode"); ESP_LOGI(TAG, "DIRECTION_FINDING Example Periodic Adv AOD Mode");
ble_direction_finding_antenna_init(antenna_use_gpio,CONFIG_EXAMPLE_ANT_GPIO_BIT_COUNT); rc = ble_direction_finding_antenna_init(antenna_use_gpio, CONFIG_EXAMPLE_ANT_GPIO_BIT_COUNT);
if (rc != 0) {
ESP_LOGE(TAG, "Antenna init failed; rc=%d", rc);
nimble_port_deinit();
return;
}
#elif defined(CONFIG_EXAMPLE_ADV_DIRECTION_FINDING_AOA) #elif defined(CONFIG_EXAMPLE_ADV_DIRECTION_FINDING_AOA)
ESP_LOGI(TAG, "DIRECTION_FINDING Example Periodic Adv AOA Mode"); ESP_LOGI(TAG, "DIRECTION_FINDING Example Periodic Adv AOA Mode");
#endif #endif
@@ -173,7 +173,7 @@ static int periodic_sync_gap_event(struct ble_gap_event *event, void *arg)
memcpy(dev_name, &adv_data[index + 2], name_len); memcpy(dev_name, &adv_data[index + 2], name_len);
dev_name[name_len] = '\0'; dev_name[name_len] = '\0';
if (strcmp(dev_name, CONFIG_SYNC_TARGET_DEVNAME) == 0) { if (strcmp(dev_name, CONFIG_EXAMPLE_SYNC_TARGET_DEVNAME) == 0) {
should_sync = true; should_sync = true;
break; break;
} }
@@ -184,7 +184,7 @@ static int periodic_sync_gap_event(struct ble_gap_event *event, void *arg)
#else #else
#error "Please select EXAMPLE_SYNC_BY_SID or EXAMPLE_SYNC_BY_NAME in menuconfig" #error "Please select EXAMPLE_SYNC_BY_SID or EXAMPLE_SYNC_BY_NAME in menuconfig"
#endif #endif
if (should_sync) { if (should_sync && disc->periodic_adv_itvl != 0) {
ble_addr_t addr; ble_addr_t addr;
memcpy(&addr, &disc->addr, sizeof(ble_addr_t)); memcpy(&addr, &disc->addr, sizeof(ble_addr_t));
@@ -249,6 +249,7 @@ static int periodic_sync_gap_event(struct ble_gap_event *event, void *arg)
static void periodic_sync_on_reset(int reason) static void periodic_sync_on_reset(int reason)
{ {
ESP_LOGE(TAG, "Resetting state; reason=%d", reason); ESP_LOGE(TAG, "Resetting state; reason=%d", reason);
is_synced = 0;
} }
static void periodic_sync_on_sync(void) static void periodic_sync_on_sync(void)
@@ -206,16 +206,17 @@ ext_ble_cts_cent_should_connect(const struct ble_gap_ext_disc_desc *disc)
{ {
int offset = 0; int offset = 0;
int ad_struct_len = 0; int ad_struct_len = 0;
uint8_t test_addr[6]; uint8_t test_addr[6] = {0};
if (disc->legacy_event_type != BLE_HCI_ADV_RPT_EVTYPE_ADV_IND && if (!(disc->props & BLE_HCI_ADV_CONN_MASK)) {
disc->legacy_event_type != BLE_HCI_ADV_RPT_EVTYPE_DIR_IND) {
return 0; return 0;
} }
if (strlen(CONFIG_EXAMPLE_PEER_ADDR) && (strncmp(CONFIG_EXAMPLE_PEER_ADDR, "ADDR_ANY", strlen("ADDR_ANY")) != 0)) { if (strlen(CONFIG_EXAMPLE_PEER_ADDR) && (strncmp(CONFIG_EXAMPLE_PEER_ADDR, "ADDR_ANY", strlen("ADDR_ANY")) != 0)) {
ESP_LOGI(tag, "Peer address from menuconfig: %s", CONFIG_EXAMPLE_PEER_ADDR); ESP_LOGI(tag, "Peer address from menuconfig: %s", CONFIG_EXAMPLE_PEER_ADDR);
/* Convert string to address */ /* Convert string to address */
peer_addr_parse(CONFIG_EXAMPLE_PEER_ADDR, test_addr); if (peer_addr_parse(CONFIG_EXAMPLE_PEER_ADDR, test_addr) != 6) {
return 0;
}
if (memcmp(test_addr, disc->addr.val, sizeof(disc->addr.val)) != 0) { if (memcmp(test_addr, disc->addr.val, sizeof(disc->addr.val)) != 0) {
return 0; return 0;
} }
@@ -224,17 +225,24 @@ ext_ble_cts_cent_should_connect(const struct ble_gap_ext_disc_desc *disc)
/* The device has to advertise support for the CTS /* The device has to advertise support for the CTS
* service (0x1805). * service (0x1805).
*/ */
do { while (offset < disc->length_data) {
if (offset + 1 >= disc->length_data) {
break;
}
ad_struct_len = disc->data[offset]; ad_struct_len = disc->data[offset];
if (!ad_struct_len) { if (!ad_struct_len) {
break; break;
} }
if (offset + ad_struct_len >= disc->length_data) {
break;
}
/* Search if cts UUID is advertised */ /* Search if cts UUID is advertised */
if (disc->data[offset + 1] == 0x03) { if (disc->data[offset + 1] == 0x03) {
int temp = 2; int temp = 2;
while (temp < ad_struct_len) { while (temp < ad_struct_len && (offset + temp + 1) < disc->length_data) {
if(disc->data[offset + temp] == 0x05 && if(disc->data[offset + temp] == 0x05 &&
disc->data[offset + temp + 1] == 0x18) { disc->data[offset + temp + 1] == 0x18) {
return 1; return 1;
@@ -243,7 +251,7 @@ ext_ble_cts_cent_should_connect(const struct ble_gap_ext_disc_desc *disc)
} }
} }
offset += ad_struct_len + 1; offset += ad_struct_len + 1;
} while ( offset < disc->length_data ); }
return 0; return 0;
} }
@@ -255,7 +263,7 @@ ble_cts_cent_should_connect(const struct ble_gap_disc_desc *disc)
struct ble_hs_adv_fields fields; struct ble_hs_adv_fields fields;
int rc; int rc;
int i; int i;
uint8_t test_addr[6]; uint8_t test_addr[6] = {0};
/* The device has to be advertising connectability. */ /* The device has to be advertising connectability. */
if (disc->event_type != BLE_HCI_ADV_RPT_EVTYPE_ADV_IND && if (disc->event_type != BLE_HCI_ADV_RPT_EVTYPE_ADV_IND &&
disc->event_type != BLE_HCI_ADV_RPT_EVTYPE_DIR_IND) { disc->event_type != BLE_HCI_ADV_RPT_EVTYPE_DIR_IND) {
@@ -271,7 +279,9 @@ ble_cts_cent_should_connect(const struct ble_gap_disc_desc *disc)
if (strlen(CONFIG_EXAMPLE_PEER_ADDR) && (strncmp(CONFIG_EXAMPLE_PEER_ADDR, "ADDR_ANY", strlen("ADDR_ANY")) != 0)) { if (strlen(CONFIG_EXAMPLE_PEER_ADDR) && (strncmp(CONFIG_EXAMPLE_PEER_ADDR, "ADDR_ANY", strlen("ADDR_ANY")) != 0)) {
ESP_LOGI(tag, "Peer address from menuconfig: %s", CONFIG_EXAMPLE_PEER_ADDR); ESP_LOGI(tag, "Peer address from menuconfig: %s", CONFIG_EXAMPLE_PEER_ADDR);
/* Convert string to address */ /* Convert string to address */
peer_addr_parse(CONFIG_EXAMPLE_PEER_ADDR, test_addr); if (peer_addr_parse(CONFIG_EXAMPLE_PEER_ADDR, test_addr) != 6) {
return 0;
}
if (memcmp(test_addr, disc->addr.val, sizeof(disc->addr.val)) != 0) { if (memcmp(test_addr, disc->addr.val, sizeof(disc->addr.val)) != 0) {
return 0; return 0;
} }
@@ -326,6 +336,9 @@ ble_cts_cent_connect_if_interesting(void *disc)
rc = ble_hs_id_infer_auto(0, &own_addr_type); rc = ble_hs_id_infer_auto(0, &own_addr_type);
if (rc != 0) { if (rc != 0) {
MODLOG_DFLT(ERROR, "error determining address type; rc=%d\n", rc); MODLOG_DFLT(ERROR, "error determining address type; rc=%d\n", rc);
#if !(MYNEWT_VAL(BLE_HOST_ALLOW_CONNECT_WITH_SCAN))
ble_cts_cent_scan();
#endif
return; return;
} }
@@ -343,6 +356,9 @@ ble_cts_cent_connect_if_interesting(void *disc)
MODLOG_DFLT(ERROR, "Error: Failed to connect to device; addr_type=%d " MODLOG_DFLT(ERROR, "Error: Failed to connect to device; addr_type=%d "
"addr=%s; rc=%d\n", "addr=%s; rc=%d\n",
addr->type, addr_str(addr->val), rc); addr->type, addr_str(addr->val), rc);
#if !(MYNEWT_VAL(BLE_HOST_ALLOW_CONNECT_WITH_SCAN))
ble_cts_cent_scan();
#endif
return; return;
} }
} }
@@ -365,11 +381,13 @@ static int
ble_cts_cent_gap_event(struct ble_gap_event *event, void *arg) ble_cts_cent_gap_event(struct ble_gap_event *event, void *arg)
{ {
struct ble_gap_conn_desc desc; struct ble_gap_conn_desc desc;
struct ble_hs_adv_fields fields;
int rc; int rc;
switch (event->type) { switch (event->type) {
case BLE_GAP_EVENT_DISC: case BLE_GAP_EVENT_DISC:
#if !CONFIG_EXAMPLE_EXTENDED_ADV
{
struct ble_hs_adv_fields fields;
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) {
@@ -381,6 +399,8 @@ ble_cts_cent_gap_event(struct ble_gap_event *event, void *arg)
/* Try to connect to the advertiser if it looks interesting. */ /* Try to connect to the advertiser if it looks interesting. */
ble_cts_cent_connect_if_interesting(&event->disc); ble_cts_cent_connect_if_interesting(&event->disc);
}
#endif
return 0; return 0;
case BLE_GAP_EVENT_CONNECT: case BLE_GAP_EVENT_CONNECT:
@@ -597,11 +617,14 @@ app_main(void)
/* Initialize data structures to track connected peers. */ /* Initialize data structures to track connected peers. */
#if MYNEWT_VAL(BLE_INCL_SVC_DISCOVERY) || MYNEWT_VAL(BLE_GATT_CACHING_INCLUDE_SERVICES) #if MYNEWT_VAL(BLE_INCL_SVC_DISCOVERY) || MYNEWT_VAL(BLE_GATT_CACHING_INCLUDE_SERVICES)
rc = peer_init(MYNEWT_VAL(BLE_MAX_CONNECTIONS), 64, 64, 64, 64); rc = peer_init(MYNEWT_VAL(BLE_MAX_CONNECTIONS), 64, 64, 64, 64);
assert(rc == 0);
#else #else
rc = peer_init(MYNEWT_VAL(BLE_MAX_CONNECTIONS), 64, 64, 64); rc = peer_init(MYNEWT_VAL(BLE_MAX_CONNECTIONS), 64, 64, 64);
assert(rc == 0);
#endif #endif
if (rc != 0) {
ESP_LOGE(tag, "Failed to init peer tracking; rc=%d", rc);
nimble_port_deinit();
return;
}
#if CONFIG_BT_NIMBLE_GAP_SERVICE #if CONFIG_BT_NIMBLE_GAP_SERVICE
/* Set the default device name. */ /* Set the default device name. */
@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2017-2025 Espressif Systems (Shanghai) CO LTD * SPDX-FileCopyrightText: 2017-2026 Espressif Systems (Shanghai) CO LTD
* *
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
@@ -71,9 +71,8 @@ int fetch_current_time(struct ble_svc_cts_curr_time *ctime) {
ctime->et_256.d_d_t.d_t.seconds = timeinfo.tm_sec; ctime->et_256.d_d_t.d_t.seconds = timeinfo.tm_sec;
/* day of week */ /* day of week */
/* time gives day range of [0, 6], current_time_sevice /* tm_wday: 0=Sunday..6=Saturday; CTS: 1=Monday..7=Sunday */
has day range of [1,7] */ ctime->et_256.d_d_t.day_of_week = (timeinfo.tm_wday == 0) ? 7 : timeinfo.tm_wday;
ctime->et_256.d_d_t.day_of_week = timeinfo.tm_wday + 1;
/* fractions_256 */ /* fractions_256 */
ctime->et_256.fractions_256 = (((uint64_t)tv_now.tv_usec * 256L )/ 1000000L); ctime->et_256.fractions_256 = (((uint64_t)tv_now.tv_usec * 256L )/ 1000000L);
@@ -108,7 +107,9 @@ int set_current_time(struct ble_svc_cts_curr_time ctime) {
timeinfo.tm_hour = ctime.et_256.d_d_t.d_t.hours; timeinfo.tm_hour = ctime.et_256.d_d_t.d_t.hours;
timeinfo.tm_min = ctime.et_256.d_d_t.d_t.minutes; timeinfo.tm_min = ctime.et_256.d_d_t.d_t.minutes;
timeinfo.tm_sec = ctime.et_256.d_d_t.d_t.seconds; timeinfo.tm_sec = ctime.et_256.d_d_t.d_t.seconds;
timeinfo.tm_wday = ctime.et_256.d_d_t.day_of_week - 1; /* CTS day_of_week: 1=Monday..7=Sunday; tm_wday: 0=Sunday..6=Saturday */
timeinfo.tm_wday = (ctime.et_256.d_d_t.day_of_week == 7) ? 0 : ctime.et_256.d_d_t.day_of_week;
timeinfo.tm_isdst = -1;
now = mktime(&timeinfo); now = mktime(&timeinfo);
if (now == (time_t)-1) { if (now == (time_t)-1) {
ESP_LOGE(TAG, "Failed to convert current time"); ESP_LOGE(TAG, "Failed to convert current time");
@@ -137,6 +138,7 @@ int set_local_time_info(struct ble_svc_cts_local_time_info info) {
local_info.timezone = info.timezone; local_info.timezone = info.timezone;
local_info.dst_offset = info.dst_offset; local_info.dst_offset = info.dst_offset;
gettimeofday(&last_updated, NULL); gettimeofday(&last_updated, NULL);
adjust_reason = (CHANGE_OF_DST_MASK | CHANGE_OF_TIME_ZONE_MASK);
return 0; return 0;
} }
int fetch_reference_time_info(struct ble_svc_cts_reference_time_info *info) { int fetch_reference_time_info(struct ble_svc_cts_reference_time_info *info) {
@@ -160,8 +162,6 @@ int fetch_reference_time_info(struct ble_svc_cts_reference_time_info *info) {
hours_since_update = (tv_now.tv_sec % 86400L) / 3600; hours_since_update = (tv_now.tv_sec % 86400L) / 3600;
info->hours_since_update = hours_since_update; info->hours_since_update = hours_since_update;
} }
adjust_reason = (CHANGE_OF_DST_MASK | CHANGE_OF_TIME_ZONE_MASK);
return 0; return 0;
} }
int int
@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2017-2025 Espressif Systems (Shanghai) CO LTD * SPDX-FileCopyrightText: 2017-2026 Espressif Systems (Shanghai) CO LTD
* *
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
@@ -79,8 +79,8 @@ ext_ble_cts_prph_advertise(void)
/* enable connectable advertising */ /* enable connectable advertising */
params.connectable = 1; params.connectable = 1;
/* advertise using random addr */ /* advertise using configured addr */
params.own_addr_type = BLE_OWN_ADDR_PUBLIC; params.own_addr_type = ble_cts_prph_addr_type;
params.primary_phy = BLE_HCI_LE_PHY_1M; params.primary_phy = BLE_HCI_LE_PHY_1M;
params.secondary_phy = BLE_HCI_LE_PHY_2M; params.secondary_phy = BLE_HCI_LE_PHY_2M;
@@ -148,9 +148,10 @@ ble_cts_prph_advertise(void)
fields.name_len = strlen(device_name); fields.name_len = strlen(device_name);
fields.name_is_complete = 1; fields.name_is_complete = 1;
fields.uuids16 = (ble_uuid16_t[]) { static const ble_uuid16_t adv_uuids16[] = {
BLE_UUID16_INIT(BLE_SVC_CTS_UUID16) BLE_UUID16_INIT(BLE_SVC_CTS_UUID16)
}; };
fields.uuids16 = adv_uuids16;
fields.num_uuids16 = 1; fields.num_uuids16 = 1;
fields.uuids16_is_complete = 1; fields.uuids16_is_complete = 1;
@@ -141,9 +141,11 @@ gatt_svc_access(uint16_t conn_handle, uint16_t attr_handle,
sizeof(gatt_svr_chr_val), sizeof(gatt_svr_chr_val),
sizeof(gatt_svr_chr_val), sizeof(gatt_svr_chr_val),
&gatt_svr_chr_val, NULL); &gatt_svr_chr_val, NULL);
ble_gatts_chr_updated(attr_handle); if (rc == 0) {
MODLOG_DFLT(INFO, "Notification/Indication scheduled for " ble_gatts_chr_updated(attr_handle);
"all subscribed peers.\n"); MODLOG_DFLT(INFO, "Notification/Indication scheduled for "
"all subscribed peers.\n");
}
return rc; return rc;
} }
goto unknown; goto unknown;
@@ -160,7 +162,7 @@ gatt_svc_access(uint16_t conn_handle, uint16_t attr_handle,
if (ble_uuid_cmp(uuid, &gatt_svr_dsc_uuid.u) == 0) { if (ble_uuid_cmp(uuid, &gatt_svr_dsc_uuid.u) == 0) {
rc = os_mbuf_append(ctxt->om, rc = os_mbuf_append(ctxt->om,
&gatt_svr_dsc_val, &gatt_svr_dsc_val,
sizeof(gatt_svr_chr_val)); sizeof(gatt_svr_dsc_val));
return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES; return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
} }
goto unknown; goto unknown;
@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD * SPDX-FileCopyrightText: 2021-2026 Espressif Systems (Shanghai) CO LTD
* *
* SPDX-License-Identifier: Unlicense OR CC0-1.0 * SPDX-License-Identifier: Unlicense OR CC0-1.0
*/ */
@@ -90,9 +90,11 @@ dynamic_service_advertise(void)
fields.name_len = strlen(name); fields.name_len = strlen(name);
fields.name_is_complete = 1; fields.name_is_complete = 1;
fields.uuids16 = (ble_uuid16_t[]) { /* Must be static: ble_gap_adv_set_fields stores the pointer, not the data.
BLE_UUID16_INIT(GATT_SVR_SVC_ALERT_UUID) * A stack compound literal becomes dangling after this function returns,
}; * causing corruption when BLE_NIMBLE_ENABLE_CONN_REATTEMPT re-uses it. */
static const ble_uuid16_t adv_uuids16[] = { BLE_UUID16_INIT(GATT_SVR_SVC_ALERT_UUID) };
fields.uuids16 = adv_uuids16;
fields.num_uuids16 = 1; fields.num_uuids16 = 1;
fields.uuids16_is_complete = 1; fields.uuids16_is_complete = 1;
@@ -143,8 +145,11 @@ dynamic_service_gap_event(struct ble_gap_event *event, void *arg)
event->connect.status); event->connect.status);
if (event->connect.status == 0) { if (event->connect.status == 0) {
rc = ble_gap_conn_find(event->connect.conn_handle, &desc); rc = ble_gap_conn_find(event->connect.conn_handle, &desc);
assert(rc == 0); if (rc != 0) {
dynamic_service_print_conn_desc(&desc); MODLOG_DFLT(ERROR, "Failed to find connection; rc=%d\n", rc);
} else {
dynamic_service_print_conn_desc(&desc);
}
} }
MODLOG_DFLT(INFO, "\n"); MODLOG_DFLT(INFO, "\n");
@@ -282,11 +287,19 @@ app_main(void)
#if MYNEWT_VAL(BLE_GATTS) #if MYNEWT_VAL(BLE_GATTS)
rc = gatt_svr_init(); rc = gatt_svr_init();
assert(rc == 0); if (rc != 0) {
MODLOG_DFLT(ERROR, "Failed to init GATT server; rc=%d\n", rc);
nimble_port_deinit();
return;
}
/* Set the default device name. */ /* Set the default device name. */
rc = ble_svc_gap_device_name_set("ble-dynamic-service"); rc = ble_svc_gap_device_name_set("ble-dynamic-service");
assert(rc == 0); if (rc != 0) {
MODLOG_DFLT(ERROR, "Failed to set device name; rc=%d\n", rc);
nimble_port_deinit();
return;
}
#endif #endif
nimble_port_freertos_init(dynamic_service_host_task); nimble_port_freertos_init(dynamic_service_host_task);
@@ -78,6 +78,7 @@ enc_adv_data_cent_on_read(uint16_t conn_handle,
{ {
int rc; int rc;
struct ble_store_value_ead value_ead = {0}; struct ble_store_value_ead value_ead = {0};
struct ble_gap_conn_desc desc;
struct peer *p; struct peer *p;
MODLOG_DFLT(INFO, "Read complete; status=%d conn_handle=%d", error->status, MODLOG_DFLT(INFO, "Read complete; status=%d conn_handle=%d", error->status,
@@ -119,6 +120,10 @@ enc_adv_data_cent_on_read(uint16_t conn_handle,
print_bytes(value_ead.km.iv, BLE_EAD_IV_SIZE); print_bytes(value_ead.km.iv, BLE_EAD_IV_SIZE);
memcpy(&value_ead.peer_addr.val, &p->peer_addr, PEER_ADDR_VAL_SIZE); memcpy(&value_ead.peer_addr.val, &p->peer_addr, PEER_ADDR_VAL_SIZE);
rc = ble_gap_conn_find(conn_handle, &desc);
if (rc == 0) {
value_ead.peer_addr.type = desc.peer_id_addr.type;
}
rc = ble_store_write_ead(&value_ead); rc = ble_store_write_ead(&value_ead);
if (rc == 0) { if (rc == 0) {
@@ -184,6 +189,8 @@ enc_adv_data_cent_on_disc_complete(const struct peer *peer, int status, void *ar
if (!enc_adv_data_check_km_exist(peer->peer_addr)) { if (!enc_adv_data_check_km_exist(peer->peer_addr)) {
/* Now perform GATT read procedures against the peer */ /* Now perform GATT read procedures against the peer */
enc_adv_data_cent_read(peer); enc_adv_data_cent_read(peer);
} else {
ble_gap_terminate(peer->conn_handle, BLE_ERR_REM_USER_CONN_TERM);
} }
} }
#endif #endif
@@ -289,7 +296,17 @@ enc_adv_data_cent_decrypt(uint8_t length_data, const uint8_t *data, const uint8_
return 0; return 0;
} }
uint8_t actual_dec_len = enc_payload_len - (BLE_EAD_RANDOMIZER_SIZE + BLE_EAD_MIC_SIZE);
if (actual_dec_len == 0) {
free(enc_data);
return 0;
}
dec_data_len = temp[0]; dec_data_len = temp[0];
if ((uint16_t)dec_data_len + 1 > actual_dec_len) {
MODLOG_DFLT(ERROR, "Decrypted length field exceeds payload");
free(enc_data);
return 0;
}
MODLOG_DFLT(INFO, "Data after decryption:"); MODLOG_DFLT(INFO, "Data after decryption:");
for (int i = 0; i < dec_data_len + 1; i++) { for (int i = 0; i < dec_data_len + 1; i++) {
@@ -318,9 +335,8 @@ enc_adv_data_cent_ext_should_connect(const struct ble_gap_ext_disc_desc *disc)
{ {
int offset = 0; int offset = 0;
int ad_struct_len = 0; int ad_struct_len = 0;
uint8_t test_addr[6]; uint8_t test_addr[6] = {0};
if (disc->legacy_event_type != BLE_HCI_ADV_RPT_EVTYPE_ADV_IND && if (!(disc->props & BLE_HCI_ADV_CONN_MASK)) {
disc->legacy_event_type != BLE_HCI_ADV_RPT_EVTYPE_DIR_IND) {
return 0; return 0;
} }
if (strlen(CONFIG_EXAMPLE_PEER_ADDR) && (strncmp(CONFIG_EXAMPLE_PEER_ADDR, "ADDR_ANY", strlen ("ADDR_ANY")) != 0)) { if (strlen(CONFIG_EXAMPLE_PEER_ADDR) && (strncmp(CONFIG_EXAMPLE_PEER_ADDR, "ADDR_ANY", strlen ("ADDR_ANY")) != 0)) {
@@ -355,6 +371,7 @@ enc_adv_data_cent_ext_should_connect(const struct ble_gap_ext_disc_desc *disc)
MODLOG_DFLT(INFO, "Adding peer addr : %s", addr_str(&disc->addr.val)); MODLOG_DFLT(INFO, "Adding peer addr : %s", addr_str(&disc->addr.val));
memcpy(&kmp[counter].peer_addr, &disc->addr.val, PEER_ADDR_VAL_SIZE); memcpy(&kmp[counter].peer_addr, &disc->addr.val, PEER_ADDR_VAL_SIZE);
kmp[counter].key_material_exist = false;
counter++; counter++;
if (counter > CONFIG_BT_NIMBLE_MAX_CONNECTIONS) { if (counter > CONFIG_BT_NIMBLE_MAX_CONNECTIONS) {
@@ -382,7 +399,7 @@ enc_adv_data_cent_should_connect(const struct ble_gap_disc_desc *disc)
struct ble_hs_adv_fields fields; struct ble_hs_adv_fields fields;
int rc; int rc;
int i; int i;
uint8_t test_addr[6]; uint8_t test_addr[6] = {0};
if (disc->event_type != BLE_HCI_ADV_RPT_EVTYPE_ADV_IND && if (disc->event_type != BLE_HCI_ADV_RPT_EVTYPE_ADV_IND &&
disc->event_type != BLE_HCI_ADV_RPT_EVTYPE_DIR_IND) { disc->event_type != BLE_HCI_ADV_RPT_EVTYPE_DIR_IND) {
return 0; return 0;
@@ -416,6 +433,7 @@ enc_adv_data_cent_should_connect(const struct ble_gap_disc_desc *disc)
MODLOG_DFLT(INFO, "Adding peer addr : %s", addr_str(&disc->addr.val)); MODLOG_DFLT(INFO, "Adding peer addr : %s", addr_str(&disc->addr.val));
memcpy(&kmp[counter].peer_addr, &disc->addr.val, PEER_ADDR_VAL_SIZE); memcpy(&kmp[counter].peer_addr, &disc->addr.val, PEER_ADDR_VAL_SIZE);
kmp[counter].key_material_exist = false;
counter++; counter++;
if (counter > CONFIG_BT_NIMBLE_MAX_CONNECTIONS) { if (counter > CONFIG_BT_NIMBLE_MAX_CONNECTIONS) {
@@ -470,6 +488,9 @@ enc_adv_data_cent_connect_if_interesting(void *disc)
rc = ble_hs_id_infer_auto(0, &own_addr_type); rc = ble_hs_id_infer_auto(0, &own_addr_type);
if (rc != 0) { if (rc != 0) {
MODLOG_DFLT(ERROR, "error determining address type; rc=%d\n", rc); MODLOG_DFLT(ERROR, "error determining address type; rc=%d\n", rc);
#if !(MYNEWT_VAL(BLE_HOST_ALLOW_CONNECT_WITH_SCAN))
enc_adv_data_cent_scan();
#endif
return; return;
} }
@@ -488,6 +509,9 @@ enc_adv_data_cent_connect_if_interesting(void *disc)
MODLOG_DFLT(ERROR, "Error: Failed to connect to device; addr_type=%d " MODLOG_DFLT(ERROR, "Error: Failed to connect to device; addr_type=%d "
"addr=%s; rc=%d\n", "addr=%s; rc=%d\n",
addr->type, addr_str(addr->val), rc); addr->type, addr_str(addr->val), rc);
#if !(MYNEWT_VAL(BLE_HOST_ALLOW_CONNECT_WITH_SCAN))
enc_adv_data_cent_scan();
#endif
return; return;
} }
} }
@@ -555,12 +579,15 @@ enc_adv_data_cent_gap_event(struct ble_gap_event *event, void *arg)
rc = peer_add(event->connect.conn_handle); rc = peer_add(event->connect.conn_handle);
if (rc != 0) { if (rc != 0) {
MODLOG_DFLT(ERROR, "Failed to add peer; rc=%d\n", rc); MODLOG_DFLT(ERROR, "Failed to add peer; rc=%d\n", rc);
ble_gap_terminate(event->connect.conn_handle, BLE_ERR_REM_USER_CONN_TERM);
return 0; return 0;
} }
rc = peer_set_addr(event->connect.conn_handle, desc.peer_id_addr.val); rc = peer_set_addr(event->connect.conn_handle, desc.peer_id_addr.val);
if (rc != 0) { if (rc != 0) {
MODLOG_DFLT(ERROR, "Failed to set peer addr; rc=%d\n", rc); MODLOG_DFLT(ERROR, "Failed to set peer addr; rc=%d\n", rc);
peer_delete(event->connect.conn_handle);
ble_gap_terminate(event->connect.conn_handle, BLE_ERR_REM_USER_CONN_TERM);
return 0; return 0;
} }
@@ -727,11 +754,14 @@ app_main(void)
/* Initialize data structures to track connected peers. */ /* Initialize data structures to track connected peers. */
#if MYNEWT_VAL(BLE_INCL_SVC_DISCOVERY) || MYNEWT_VAL(BLE_GATT_CACHING_INCLUDE_SERVICES) #if MYNEWT_VAL(BLE_INCL_SVC_DISCOVERY) || MYNEWT_VAL(BLE_GATT_CACHING_INCLUDE_SERVICES)
rc = peer_init(MYNEWT_VAL(BLE_MAX_CONNECTIONS), 64, 64, 64, 64); rc = peer_init(MYNEWT_VAL(BLE_MAX_CONNECTIONS), 64, 64, 64, 64);
assert(rc == 0);
#else #else
rc = peer_init(MYNEWT_VAL(BLE_MAX_CONNECTIONS), 64, 64, 64); rc = peer_init(MYNEWT_VAL(BLE_MAX_CONNECTIONS), 64, 64, 64);
assert(rc == 0);
#endif #endif
if (rc != 0) {
ESP_LOGE(tag, "Failed to init peer tracking; rc=%d", rc);
nimble_port_deinit();
return;
}
#if CONFIG_BT_NIMBLE_GAP_SERVICE #if CONFIG_BT_NIMBLE_GAP_SERVICE
/* Set the default device name. */ /* Set the default device name. */
@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2021-2023 Espressif Systems (Shanghai) CO LTD * SPDX-FileCopyrightText: 2021-2026 Espressif Systems (Shanghai) CO LTD
* *
* SPDX-License-Identifier: Unlicense OR CC0-1.0 * SPDX-License-Identifier: Unlicense OR CC0-1.0
*/ */
@@ -150,9 +150,11 @@ gatt_svc_access(uint16_t conn_handle, uint16_t attr_handle,
sizeof(gatt_svr_chr_val), sizeof(gatt_svr_chr_val),
sizeof(gatt_svr_chr_val), sizeof(gatt_svr_chr_val),
&gatt_svr_chr_val, NULL); &gatt_svr_chr_val, NULL);
ble_gatts_chr_updated(attr_handle); if (rc == 0) {
MODLOG_DFLT(INFO, "Notification/Indication scheduled for " ble_gatts_chr_updated(attr_handle);
"all subscribed peers.\n"); MODLOG_DFLT(INFO, "Notification/Indication scheduled for "
"all subscribed peers.\n");
}
return rc; return rc;
} }
goto unknown; goto unknown;
@@ -169,7 +171,7 @@ gatt_svc_access(uint16_t conn_handle, uint16_t attr_handle,
if (ble_uuid_cmp(uuid, &gatt_svr_dsc_uuid.u) == 0) { if (ble_uuid_cmp(uuid, &gatt_svr_dsc_uuid.u) == 0) {
rc = os_mbuf_append(ctxt->om, rc = os_mbuf_append(ctxt->om,
&gatt_svr_dsc_val, &gatt_svr_dsc_val,
sizeof(gatt_svr_chr_val)); sizeof(gatt_svr_dsc_val));
return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES; return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
} }
goto unknown; goto unknown;
@@ -140,8 +140,8 @@ enc_adv_data_prph_ext_advertise(void)
/* enable connectable advertising */ /* enable connectable advertising */
params.connectable = 1; params.connectable = 1;
/* advertise using random addr */ /* advertise using configured addr */
params.own_addr_type = BLE_OWN_ADDR_PUBLIC; params.own_addr_type = own_addr_type;
params.primary_phy = BLE_HCI_LE_PHY_1M; params.primary_phy = BLE_HCI_LE_PHY_1M;
params.secondary_phy = BLE_HCI_LE_PHY_2M; params.secondary_phy = BLE_HCI_LE_PHY_2M;
@@ -163,6 +163,10 @@ enc_adv_data_prph_ext_advertise(void)
/* get mbuf with adv data */ /* get mbuf with adv data */
temp = malloc(sizeof(ext_adv_pattern) + 2 + encrypted_adv_data_len); temp = malloc(sizeof(ext_adv_pattern) + 2 + encrypted_adv_data_len);
if (temp == NULL) {
MODLOG_DFLT(ERROR, "Failed to allocate temp buffer for ext adv data");
return;
}
memcpy(temp, ext_adv_pattern, sizeof(ext_adv_pattern)); memcpy(temp, ext_adv_pattern, sizeof(ext_adv_pattern));
temp[sizeof(ext_adv_pattern)] = 1 + encrypted_adv_data_len; temp[sizeof(ext_adv_pattern)] = 1 + encrypted_adv_data_len;
temp[sizeof(ext_adv_pattern) + 1] = BLE_GAP_ENC_ADV_DATA; temp[sizeof(ext_adv_pattern) + 1] = BLE_GAP_ENC_ADV_DATA;
@@ -194,8 +198,11 @@ enc_adv_data_prph_advertise(void)
struct ble_hs_adv_fields fields; struct ble_hs_adv_fields fields;
int rc; int rc;
const unsigned encrypted_adv_data_len = BLE_EAD_ENCRYPTED_PAYLOAD_SIZE(sizeof(unencrypted_adv_pattern)); static const ble_uuid16_t adv_uuids16[] = {
uint8_t encrypted_adv_data[encrypted_adv_data_len]; BLE_UUID16_INIT(0x2C01) /** For the central to recognise this device */
};
static uint8_t encrypted_adv_data[BLE_EAD_ENCRYPTED_PAYLOAD_SIZE(sizeof(unencrypted_adv_pattern))];
const unsigned encrypted_adv_data_len = sizeof(encrypted_adv_data);
memset(encrypted_adv_data, 0, encrypted_adv_data_len); memset(encrypted_adv_data, 0, encrypted_adv_data_len);
/* First check if any instance is already active */ /* First check if any instance is already active */
@@ -207,8 +214,6 @@ enc_adv_data_prph_advertise(void)
memset (&params, 0, sizeof(params)); memset (&params, 0, sizeof(params));
memset (&fields, 0, sizeof(fields)); memset (&fields, 0, sizeof(fields));
own_addr_type = BLE_OWN_ADDR_PUBLIC;
/* enable connectable advertising */ /* enable connectable advertising */
params.conn_mode = BLE_GAP_CONN_MODE_UND; params.conn_mode = BLE_GAP_CONN_MODE_UND;
params.disc_mode = BLE_GAP_DISC_MODE_GEN; params.disc_mode = BLE_GAP_DISC_MODE_GEN;
@@ -222,9 +227,7 @@ enc_adv_data_prph_advertise(void)
fields.name_len = 3; fields.name_len = 3;
fields.name_is_complete = 1; fields.name_is_complete = 1;
fields.uuids16 = (ble_uuid16_t[]) { fields.uuids16 = adv_uuids16;
BLE_UUID16_INIT(0x2C01) /** For the central to recognise this device */
};
fields.num_uuids16 = 1; fields.num_uuids16 = 1;
fields.uuids16_is_complete = 1; fields.uuids16_is_complete = 1;
@@ -397,17 +400,22 @@ enc_adv_data_prph_on_reset(int reason)
static void static void
ble_app_set_addr(void) ble_app_set_addr(void)
{ {
ble_addr_t addr; ble_addr_t addr = {0};
int rc; int rc;
/* generate new non-resolvable private address */ /* generate new non-resolvable private address */
rc = ble_hs_id_gen_rnd(0, &addr); rc = ble_hs_id_gen_rnd(0, &addr);
assert(rc == 0); if (rc != 0) {
MODLOG_DFLT(ERROR, "Failed to generate random address; rc=%d\n", rc);
return;
}
/* set generated address */ /* set generated address */
rc = ble_hs_id_set_rnd(addr.val); rc = ble_hs_id_set_rnd(addr.val);
if (rc != 0) {
assert(rc == 0); MODLOG_DFLT(ERROR, "Failed to set random address; rc=%d\n", rc);
return;
}
} }
#endif #endif
@@ -17,16 +17,16 @@ static uint8_t own_addr_type;
static int blecoex_gap_event(struct ble_gap_event *event, void *arg); static int blecoex_gap_event(struct ble_gap_event *event, void *arg);
void ble_coex_advertise(void); void ble_coex_advertise(void);
void ble_coex_scan(void); void ble_coex_scan(void);
static bool client_connect = 0; static uint8_t client_connect = 0;
static int gatt_svr_access_cb(uint16_t conn_handle, uint16_t attr_handle, static int gatt_svr_access_cb(uint16_t conn_handle, uint16_t attr_handle,
struct ble_gatt_access_ctxt *ctxt, void *arg) { struct ble_gatt_access_ctxt *ctxt, void *arg) {
int rc = 0; int rc = 0;
if (ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR) { if (ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR) {
static uint8_t data[1] = {42}; // Example value static uint8_t data[1] = {42}; // Example value
os_mbuf_append(ctxt->om, data, sizeof(data)); rc = os_mbuf_append(ctxt->om, data, sizeof(data));
if (rc != 0) { if (rc != 0) {
return rc; return BLE_ATT_ERR_INSUFFICIENT_RES;
} }
} else if (ctxt->op == BLE_GATT_ACCESS_OP_READ_DSC) { } else if (ctxt->op == BLE_GATT_ACCESS_OP_READ_DSC) {
static uint8_t dsc_data[1] = {0x01}; // Example descriptor value static uint8_t dsc_data[1] = {0x01}; // Example descriptor value
@@ -192,6 +192,10 @@ blecent_on_write(uint16_t conn_handle,
uint8_t value[2]; uint8_t value[2];
int rc; int rc;
const struct peer *peer = peer_find(conn_handle); const struct peer *peer = peer_find(conn_handle);
if (peer == NULL) {
MODLOG_DFLT(ERROR, "Peer not found for conn_handle=%d\n", conn_handle);
return ble_gap_terminate(conn_handle, BLE_ERR_REM_USER_CONN_TERM);
}
dsc = peer_dsc_find_uuid(peer, dsc = peer_dsc_find_uuid(peer,
BLE_UUID16_DECLARE(BLECOEX_SVC_ALERT_UUID), BLE_UUID16_DECLARE(BLECOEX_SVC_ALERT_UUID),
@@ -216,7 +220,7 @@ blecent_on_write(uint16_t conn_handle,
return 0; return 0;
err: err:
/* Terminate the connection. */ /* Terminate the connection. */
return ble_gap_terminate(peer->conn_handle, BLE_ERR_REM_USER_CONN_TERM); return ble_gap_terminate(conn_handle, BLE_ERR_REM_USER_CONN_TERM);
} }
@@ -241,6 +245,10 @@ blecent_on_read(uint16_t conn_handle,
uint8_t value[2]; uint8_t value[2];
int rc; int rc;
const struct peer *peer = peer_find(conn_handle); const struct peer *peer = peer_find(conn_handle);
if (peer == NULL) {
MODLOG_DFLT(ERROR, "Peer not found for conn_handle=%d\n", conn_handle);
return ble_gap_terminate(conn_handle, BLE_ERR_REM_USER_CONN_TERM);
}
chr = peer_chr_find_uuid(peer, chr = peer_chr_find_uuid(peer,
BLE_UUID16_DECLARE(BLECOEX_SVC_ALERT_UUID), BLE_UUID16_DECLARE(BLECOEX_SVC_ALERT_UUID),
@@ -263,7 +271,7 @@ blecent_on_read(uint16_t conn_handle,
return 0; return 0;
err: err:
/* Terminate the connection. */ /* Terminate the connection. */
return ble_gap_terminate(peer->conn_handle, BLE_ERR_REM_USER_CONN_TERM); return ble_gap_terminate(conn_handle, BLE_ERR_REM_USER_CONN_TERM);
} }
@@ -369,23 +377,40 @@ blecoex_gap_event(struct ble_gap_event *event, void *arg)
MODLOG_DFLT(INFO, "%02x:%02x:%02x:%02x:%02x:%02x", MODLOG_DFLT(INFO, "%02x:%02x:%02x:%02x:%02x:%02x",
u8p[5], u8p[4], u8p[3], u8p[2], u8p[1], u8p[0]); u8p[5], u8p[4], u8p[3], u8p[2], u8p[1], u8p[0]);
} if (client_connect == 1) {
client_connect = 2;
if (client_connect == 1 ) { /* Remember peer. */
client_connect = 2; rc = peer_add(event->connect.conn_handle);
if (rc != 0) {
MODLOG_DFLT(ERROR, "Failed to add peer; rc=%d\n", rc);
rc = ble_gap_terminate(event->connect.conn_handle,
BLE_ERR_REM_USER_CONN_TERM);
if (rc != 0) {
MODLOG_DFLT(ERROR, "Failed to terminate connection; rc=%d\n", rc);
restart_coex();
}
return 0;
}
/* Remember peer. */ /* Perform service discovery */
rc = peer_add(event->connect.conn_handle); rc = peer_disc_all(event->connect.conn_handle,
if (rc != 0) { blecent_on_disc_complete, NULL);
MODLOG_DFLT(ERROR, "Failed to add peer; rc=%d\n", rc); if(rc != 0) {
restart_coex(); MODLOG_DFLT(ERROR, "Failed to discover services; rc=%d\n", rc);
peer_delete(event->connect.conn_handle);
rc = ble_gap_terminate(event->connect.conn_handle,
BLE_ERR_REM_USER_CONN_TERM);
if (rc != 0) {
MODLOG_DFLT(ERROR, "Failed to terminate connection; rc=%d\n", rc);
restart_coex();
}
return 0;
}
} }
} else {
/* Perform service discovery */ if (client_connect == 1) {
rc = peer_disc_all(event->connect.conn_handle, MODLOG_DFLT(ERROR, "Client connection failed; status=%d\n", event->connect.status);
blecent_on_disc_complete, NULL);
if(rc != 0) {
MODLOG_DFLT(ERROR, "Failed to discover services; rc=%d\n", rc);
restart_coex(); restart_coex();
} }
} }
@@ -393,6 +418,7 @@ blecoex_gap_event(struct ble_gap_event *event, void *arg)
case BLE_GAP_EVENT_DISCONNECT: case BLE_GAP_EVENT_DISCONNECT:
MODLOG_DFLT(INFO, "Disconnect \n"); MODLOG_DFLT(INFO, "Disconnect \n");
peer_delete(event->disconnect.conn.conn_handle);
restart_coex(); restart_coex();
return 0; return 0;
@@ -497,9 +523,10 @@ ble_coex_advertise(void)
fields.name_is_complete = 1; fields.name_is_complete = 1;
} }
fields.uuids16 = (ble_uuid16_t[]) { static const ble_uuid16_t adv_uuids16[] = {
BLE_UUID16_INIT(BLECOEX_SVC_ALERT_UUID) BLE_UUID16_INIT(BLECOEX_SVC_ALERT_UUID)
}; };
fields.uuids16 = adv_uuids16;
fields.num_uuids16 = 1; fields.num_uuids16 = 1;
fields.uuids16_is_complete = 1; fields.uuids16_is_complete = 1;
@@ -569,11 +596,17 @@ void app_main(void)
} }
ble_hs_cfg.sync_cb = on_sync; ble_hs_cfg.sync_cb = on_sync;
int rc;
#if MYNEWT_VAL(BLE_INCL_SVC_DISCOVERY) || MYNEWT_VAL(BLE_GATT_CACHING_INCLUDE_SERVICES) #if MYNEWT_VAL(BLE_INCL_SVC_DISCOVERY) || MYNEWT_VAL(BLE_GATT_CACHING_INCLUDE_SERVICES)
peer_init(MYNEWT_VAL(BLE_MAX_CONNECTIONS), 64, 64, 64, 64); rc = peer_init(MYNEWT_VAL(BLE_MAX_CONNECTIONS), 64, 64, 64, 64);
#else #else
peer_init(MYNEWT_VAL(BLE_MAX_CONNECTIONS), 64, 64, 64); rc = peer_init(MYNEWT_VAL(BLE_MAX_CONNECTIONS), 64, 64, 64);
#endif #endif
if (rc != 0) {
MODLOG_DFLT(ERROR, "Failed to init peer tracking; rc=%d\n", rc);
nimble_port_deinit();
return;
}
#if CONFIG_BT_NIMBLE_GAP_SERVICE #if CONFIG_BT_NIMBLE_GAP_SERVICE
ble_svc_gap_init(); ble_svc_gap_init();
@@ -585,8 +618,18 @@ void app_main(void)
ble_svc_ans_init(); ble_svc_ans_init();
#endif #endif
ble_gatts_count_cfg(gatt_svr_svcs); rc = ble_gatts_count_cfg(gatt_svr_svcs);
ble_gatts_add_svcs(gatt_svr_svcs); if (rc != 0) {
MODLOG_DFLT(ERROR, "ble_gatts_count_cfg failed; rc=%d\n", rc);
nimble_port_deinit();
return;
}
rc = ble_gatts_add_svcs(gatt_svr_svcs);
if (rc != 0) {
MODLOG_DFLT(ERROR, "ble_gatts_add_svcs failed; rc=%d\n", rc);
nimble_port_deinit();
return;
}
nimble_port_freertos_init(ble_hs_task); nimble_port_freertos_init(ble_hs_task);
} }
@@ -63,6 +63,15 @@ ble_htp_cent_on_subscribe(uint16_t conn_handle,
uint8_t value[2]; uint8_t value[2];
int rc; int rc;
const struct peer *peer = peer_find(conn_handle); const struct peer *peer = peer_find(conn_handle);
if (peer == NULL) {
MODLOG_DFLT(ERROR, "Peer not found for conn_handle=%d\n", conn_handle);
return ble_gap_terminate(conn_handle, BLE_ERR_REM_USER_CONN_TERM);
}
if (error->status != 0) {
MODLOG_DFLT(ERROR, "Subscribe to temp measurement failed; status=%d\n", error->status);
return ble_gap_terminate(conn_handle, BLE_ERR_REM_USER_CONN_TERM);
}
dsc = peer_dsc_find_uuid(peer, dsc = peer_dsc_find_uuid(peer,
BLE_UUID16_DECLARE(BLE_SVC_HTP_UUID16), BLE_UUID16_DECLARE(BLE_SVC_HTP_UUID16),
@@ -86,9 +95,7 @@ ble_htp_cent_on_subscribe(uint16_t conn_handle,
return 0; return 0;
err: err:
/* Terminate the connection. */ /* Terminate the connection. */
return ble_gap_terminate(peer->conn_handle, BLE_ERR_REM_USER_CONN_TERM); return ble_gap_terminate(conn_handle, BLE_ERR_REM_USER_CONN_TERM);
return 0;
} }
/** /**
@@ -113,6 +120,15 @@ ble_htp_cent_on_write(uint16_t conn_handle,
uint8_t value[2]; uint8_t value[2];
int rc; int rc;
const struct peer *peer = peer_find(conn_handle); const struct peer *peer = peer_find(conn_handle);
if (peer == NULL) {
MODLOG_DFLT(ERROR, "Peer not found for conn_handle=%d\n", conn_handle);
return ble_gap_terminate(conn_handle, BLE_ERR_REM_USER_CONN_TERM);
}
if (error->status != 0) {
MODLOG_DFLT(ERROR, "Write to measurement interval failed; status=%d\n", error->status);
return ble_gap_terminate(conn_handle, BLE_ERR_REM_USER_CONN_TERM);
}
dsc = peer_dsc_find_uuid(peer, dsc = peer_dsc_find_uuid(peer,
BLE_UUID16_DECLARE(BLE_SVC_HTP_UUID16), BLE_UUID16_DECLARE(BLE_SVC_HTP_UUID16),
@@ -137,7 +153,7 @@ ble_htp_cent_on_write(uint16_t conn_handle,
return 0; return 0;
err: err:
/* Terminate the connection. */ /* Terminate the connection. */
return ble_gap_terminate(peer->conn_handle, BLE_ERR_REM_USER_CONN_TERM); return ble_gap_terminate(conn_handle, BLE_ERR_REM_USER_CONN_TERM);
} }
/** /**
@@ -155,6 +171,9 @@ ble_htp_cent_on_read(uint16_t conn_handle,
if (error->status == 0) { if (error->status == 0) {
MODLOG_DFLT(INFO, " attr_handle=%d value=", attr->handle); MODLOG_DFLT(INFO, " attr_handle=%d value=", attr->handle);
print_mbuf(attr->om); print_mbuf(attr->om);
} else {
MODLOG_DFLT(ERROR, "Read temperature type failed; status=%d\n", error->status);
return ble_gap_terminate(conn_handle, BLE_ERR_REM_USER_CONN_TERM);
} }
MODLOG_DFLT(INFO, "\n"); MODLOG_DFLT(INFO, "\n");
@@ -319,18 +338,15 @@ ext_ble_htp_cent_should_connect(const struct ble_gap_ext_disc_desc *disc)
{ {
int offset = 0; int offset = 0;
int ad_struct_len = 0; int ad_struct_len = 0;
uint8_t test_addr[6]; uint8_t test_addr[6] = {0};
uint8_t parsed_addr[6]; if (!(disc->props & BLE_HCI_ADV_CONN_MASK)) {
if (disc->legacy_event_type != BLE_HCI_ADV_RPT_EVTYPE_ADV_IND &&
disc->legacy_event_type != BLE_HCI_ADV_RPT_EVTYPE_DIR_IND) {
return 0; return 0;
} }
if (strlen(CONFIG_EXAMPLE_PEER_ADDR) && (strncmp(CONFIG_EXAMPLE_PEER_ADDR, "ADDR_ANY", strlen ("ADDR_ANY")) != 0)) { if (strlen(CONFIG_EXAMPLE_PEER_ADDR) && (strncmp(CONFIG_EXAMPLE_PEER_ADDR, "ADDR_ANY", strlen ("ADDR_ANY")) != 0)) {
ESP_LOGI(tag, "Peer address from menuconfig: %s", CONFIG_EXAMPLE_PEER_ADDR); ESP_LOGI(tag, "Peer address from menuconfig: %s", CONFIG_EXAMPLE_PEER_ADDR);
/* Convert string to address */ /* Convert string to address */
peer_addr_parse(CONFIG_EXAMPLE_PEER_ADDR, parsed_addr); if (peer_addr_parse(CONFIG_EXAMPLE_PEER_ADDR, test_addr) != 6) {
for (int i = 0; i < 6; i++) { return 0;
test_addr[i] = parsed_addr[5 - i];
} }
if (memcmp(test_addr, disc->addr.val, sizeof(disc->addr.val)) != 0) { if (memcmp(test_addr, disc->addr.val, sizeof(disc->addr.val)) != 0) {
@@ -349,9 +365,9 @@ ext_ble_htp_cent_should_connect(const struct ble_gap_ext_disc_desc *disc)
break; break;
} }
/* Search if HTP UUID is advertised */ /* Search if HTP UUID is advertised (little-endian: 0x09 0x18 = 0x1809) */
if (disc->data[offset] == 0x03 && disc->data[offset + 1] == 0x03) { if (disc->data[offset] == 0x03 && disc->data[offset + 1] == 0x03) {
if ( disc->data[offset + 2] == 0x18 && disc->data[offset + 3] == 0x09 ) { if ( disc->data[offset + 2] == 0x09 && disc->data[offset + 3] == 0x18 ) {
return 1; return 1;
} }
} }
@@ -369,7 +385,7 @@ ble_htp_cent_should_connect(const struct ble_gap_disc_desc *disc)
struct ble_hs_adv_fields fields; struct ble_hs_adv_fields fields;
int rc; int rc;
int i; int i;
uint8_t test_addr[6]; uint8_t test_addr[6] = {0};
/* The device has to be advertising connectability. */ /* The device has to be advertising connectability. */
if (disc->event_type != BLE_HCI_ADV_RPT_EVTYPE_ADV_IND && if (disc->event_type != BLE_HCI_ADV_RPT_EVTYPE_ADV_IND &&
disc->event_type != BLE_HCI_ADV_RPT_EVTYPE_DIR_IND) { disc->event_type != BLE_HCI_ADV_RPT_EVTYPE_DIR_IND) {
@@ -385,7 +401,9 @@ ble_htp_cent_should_connect(const struct ble_gap_disc_desc *disc)
if (strlen(CONFIG_EXAMPLE_PEER_ADDR) && (strncmp(CONFIG_EXAMPLE_PEER_ADDR, "ADDR_ANY", strlen("ADDR_ANY")) != 0)) { if (strlen(CONFIG_EXAMPLE_PEER_ADDR) && (strncmp(CONFIG_EXAMPLE_PEER_ADDR, "ADDR_ANY", strlen("ADDR_ANY")) != 0)) {
ESP_LOGI(tag, "Peer address from menuconfig: %s", CONFIG_EXAMPLE_PEER_ADDR); ESP_LOGI(tag, "Peer address from menuconfig: %s", CONFIG_EXAMPLE_PEER_ADDR);
/* Convert string to address */ /* Convert string to address */
peer_addr_parse(CONFIG_EXAMPLE_PEER_ADDR, test_addr); if (peer_addr_parse(CONFIG_EXAMPLE_PEER_ADDR, test_addr) != 6) {
return 0;
}
if (memcmp(test_addr, disc->addr.val, sizeof(disc->addr.val)) != 0) { if (memcmp(test_addr, disc->addr.val, sizeof(disc->addr.val)) != 0) {
return 0; return 0;
} }
@@ -440,6 +458,9 @@ ble_htp_cent_connect_if_interesting(void *disc)
rc = ble_hs_id_infer_auto(0, &own_addr_type); rc = ble_hs_id_infer_auto(0, &own_addr_type);
if (rc != 0) { if (rc != 0) {
MODLOG_DFLT(ERROR, "error determining address type; rc=%d\n", rc); MODLOG_DFLT(ERROR, "error determining address type; rc=%d\n", rc);
#if !(MYNEWT_VAL(BLE_HOST_ALLOW_CONNECT_WITH_SCAN))
ble_htp_cent_scan();
#endif
return; return;
} }
@@ -457,6 +478,9 @@ ble_htp_cent_connect_if_interesting(void *disc)
MODLOG_DFLT(ERROR, "Error: Failed to connect to device; addr_type=%d " MODLOG_DFLT(ERROR, "Error: Failed to connect to device; addr_type=%d "
"addr=%s; rc=%d\n", "addr=%s; rc=%d\n",
addr->type, addr_str(addr->val), rc); addr->type, addr_str(addr->val), rc);
#if !(MYNEWT_VAL(BLE_HOST_ALLOW_CONNECT_WITH_SCAN))
ble_htp_cent_scan();
#endif
return; return;
} }
} }
@@ -479,11 +503,13 @@ static int
ble_htp_cent_gap_event(struct ble_gap_event *event, void *arg) ble_htp_cent_gap_event(struct ble_gap_event *event, void *arg)
{ {
struct ble_gap_conn_desc desc; struct ble_gap_conn_desc desc;
struct ble_hs_adv_fields fields;
int rc; int rc;
switch (event->type) { switch (event->type) {
case BLE_GAP_EVENT_DISC: case BLE_GAP_EVENT_DISC:
#if !CONFIG_EXAMPLE_EXTENDED_ADV
{
struct ble_hs_adv_fields fields;
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) {
@@ -495,6 +521,8 @@ ble_htp_cent_gap_event(struct ble_gap_event *event, void *arg)
/* Try to connect to the advertiser if it looks interesting. */ /* Try to connect to the advertiser if it looks interesting. */
ble_htp_cent_connect_if_interesting(&event->disc); ble_htp_cent_connect_if_interesting(&event->disc);
}
#endif
return 0; return 0;
case BLE_GAP_EVENT_CONNECT: case BLE_GAP_EVENT_CONNECT:
@@ -727,11 +755,14 @@ app_main(void)
/* Initialize data structures to track connected peers. */ /* Initialize data structures to track connected peers. */
#if MYNEWT_VAL(BLE_INCL_SVC_DISCOVERY) || MYNEWT_VAL(BLE_GATT_CACHING_INCLUDE_SERVICES) #if MYNEWT_VAL(BLE_INCL_SVC_DISCOVERY) || MYNEWT_VAL(BLE_GATT_CACHING_INCLUDE_SERVICES)
rc = peer_init(MYNEWT_VAL(BLE_MAX_CONNECTIONS), 64, 64, 64, 64); rc = peer_init(MYNEWT_VAL(BLE_MAX_CONNECTIONS), 64, 64, 64, 64);
assert(rc == 0);
#else #else
rc = peer_init(MYNEWT_VAL(BLE_MAX_CONNECTIONS), 64, 64, 64); rc = peer_init(MYNEWT_VAL(BLE_MAX_CONNECTIONS), 64, 64, 64);
assert(rc == 0);
#endif #endif
if (rc != 0) {
ESP_LOGE(tag, "Failed to init peer tracking; rc=%d", rc);
nimble_port_deinit();
return;
}
#if CONFIG_BT_NIMBLE_GAP_SERVICE #if CONFIG_BT_NIMBLE_GAP_SERVICE
/* Set the default device name. */ /* Set the default device name. */
rc = ble_svc_gap_device_name_set("nimble-htp-cent"); rc = ble_svc_gap_device_name_set("nimble-htp-cent");
@@ -15,7 +15,7 @@
static const char *manuf_name = "ESP32 devkitC"; static const char *manuf_name = "ESP32 devkitC";
static const char *model_num = "HTP Sensor demo"; static const char *model_num = "HTP Sensor demo";
static const char *system_id = "HTP1"; static const uint8_t system_id[8] = { 'H', 'T', 'P', '1' };
static int static int
gatt_svr_chr_access_device_info(uint16_t conn_handle, uint16_t attr_handle, gatt_svr_chr_access_device_info(uint16_t conn_handle, uint16_t attr_handle,
struct ble_gatt_access_ctxt *ctxt, void *arg); struct ble_gatt_access_ctxt *ctxt, void *arg);
@@ -72,7 +72,7 @@ gatt_svr_chr_access_device_info(uint16_t conn_handle, uint16_t attr_handle,
} }
if (uuid == GATT_DIS_CHR_UUID16_SYS_ID) { if (uuid == GATT_DIS_CHR_UUID16_SYS_ID) {
rc = os_mbuf_append(ctxt->om, system_id, strlen(system_id)); rc = os_mbuf_append(ctxt->om, system_id, sizeof(system_id));
return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES; return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
} }
@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2017-2025 Espressif Systems (Shanghai) CO LTD * SPDX-FileCopyrightText: 2017-2026 Espressif Systems (Shanghai) CO LTD
* *
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
@@ -20,7 +20,7 @@
static uint8_t ext_adv_pattern_1[] = { static uint8_t ext_adv_pattern_1[] = {
0x02, BLE_HS_ADV_TYPE_FLAGS, 0x06, 0x02, BLE_HS_ADV_TYPE_FLAGS, 0x06,
0x03, BLE_HS_ADV_TYPE_COMP_UUIDS16, 0xab, 0xcd, 0x03, BLE_HS_ADV_TYPE_COMP_UUIDS16, 0xab, 0xcd,
0x03, BLE_HS_ADV_TYPE_COMP_UUIDS16, 0x18, 0x09, 0x03, BLE_HS_ADV_TYPE_COMP_UUIDS16, 0x09, 0x18,
0x12, BLE_HS_ADV_TYPE_COMP_NAME, 'n', 'i', 'm', 'b', 'l', 'e', '-', 'h', 't', 'p', '-', 'p', 'r', 'p', 'h', '-', 'e', 0x12, BLE_HS_ADV_TYPE_COMP_NAME, 'n', 'i', 'm', 'b', 'l', 'e', '-', 'h', 't', 'p', '-', 'p', 'r', 'p', 'h', '-', 'e',
}; };
#endif #endif
@@ -81,8 +81,8 @@ ext_ble_htp_prph_advertise(void)
/* enable connectable advertising */ /* enable connectable advertising */
params.connectable = 1; params.connectable = 1;
/* advertise using random addr */ /* advertise using configured addr */
params.own_addr_type = BLE_OWN_ADDR_PUBLIC; params.own_addr_type = ble_htp_prph_addr_type;
params.primary_phy = BLE_HCI_LE_PHY_1M; params.primary_phy = BLE_HCI_LE_PHY_1M;
params.secondary_phy = BLE_HCI_LE_PHY_2M; params.secondary_phy = BLE_HCI_LE_PHY_2M;
@@ -150,9 +150,10 @@ ble_htp_prph_advertise(void)
fields.name_len = strlen(device_name); fields.name_len = strlen(device_name);
fields.name_is_complete = 1; fields.name_is_complete = 1;
fields.uuids16 = (ble_uuid16_t[]) { static const ble_uuid16_t adv_uuids16[] = {
BLE_UUID16_INIT(BLE_SVC_HTP_UUID16) BLE_UUID16_INIT(BLE_SVC_HTP_UUID16)
}; };
fields.uuids16 = adv_uuids16;
fields.num_uuids16 = 1; fields.num_uuids16 = 1;
fields.uuids16_is_complete = 1; fields.uuids16_is_complete = 1;
@@ -178,23 +179,16 @@ ble_htp_prph_advertise(void)
static void static void
ble_htp_prph_tx_htp_stop(void) ble_htp_prph_tx_htp_stop(void)
{ {
xTimerStop( ble_htp_prph_tx_timer, 1000 / portTICK_PERIOD_MS ); xTimerStop( ble_htp_prph_tx_timer, 0 );
} }
/* Reset temperature measurement */ /* Reset temperature measurement */
static void static void
ble_htp_prph_tx_htp_reset(void) ble_htp_prph_tx_htp_reset(void)
{ {
int rc; if (xTimerReset(ble_htp_prph_tx_timer, 0) != pdPASS) {
MODLOG_DFLT(ERROR, "Failed to reset HTP timer\n");
if (xTimerReset(ble_htp_prph_tx_timer, 1000 / portTICK_PERIOD_MS ) == pdPASS) {
rc = 0;
} else {
rc = 1;
} }
assert(rc == 0);
} }
/* This function notifies intermediate temperature to the client */ /* This function notifies intermediate temperature to the client */
@@ -372,6 +366,11 @@ void app_main(void)
/* name, period/time, auto reload, timer ID, callback */ /* name, period/time, auto reload, timer ID, callback */
ble_htp_prph_tx_timer = xTimerCreate("ble_htp_prph_tx_timer", pdMS_TO_TICKS(1000), pdTRUE, ble_htp_prph_tx_timer = xTimerCreate("ble_htp_prph_tx_timer", pdMS_TO_TICKS(1000), pdTRUE,
(void *)0, ble_htp_prph_tx); (void *)0, ble_htp_prph_tx);
if (ble_htp_prph_tx_timer == NULL) {
MODLOG_DFLT(ERROR, "Failed to create HTP TX timer\n");
nimble_port_deinit();
return;
}
#if MYNEWT_VAL(BLE_GATTS) #if MYNEWT_VAL(BLE_GATTS)
int rc; int rc;
@@ -68,16 +68,9 @@ blecent_l2cap_coc_send_data(struct ble_l2cap_chan *chan)
rc = ble_l2cap_send(chan, sdu_rx_data); rc = ble_l2cap_send(chan, sdu_rx_data);
retry = 0;
while (rc == BLE_HS_ESTALLED && retry < max_retry) {
MODLOG_DFLT(INFO, "Send stalled, waiting for credits (retry=%d)", retry);
vTaskDelay(100 / portTICK_PERIOD_MS);
rc = ble_l2cap_send(chan, sdu_rx_data);
retry++;
}
if (rc == BLE_HS_ESTALLED) { if (rc == BLE_HS_ESTALLED) {
MODLOG_DFLT(INFO, "Send still stalled after %d retries, returning", retry); /* Stack took ownership of sdu_rx_data; wait for BLE_L2CAP_EVENT_COC_TX_UNSTALLED */
MODLOG_DFLT(INFO, "Send stalled, waiting for credits");
return; return;
} }
@@ -115,7 +108,7 @@ blecent_l2cap_coc_on_disc_complete(const struct peer *peer, int status, void *ar
rc = ble_l2cap_connect(conn_handle_coc, psm, MTU, sdu_rx, blecent_l2cap_coc_event_cb, NULL); rc = ble_l2cap_connect(conn_handle_coc, psm, MTU, sdu_rx, blecent_l2cap_coc_event_cb, NULL);
if (rc != 0) { if (rc != 0) {
MODLOG_DFLT(ERROR, "L2CAP COC connect failed, rc=%d", rc); MODLOG_DFLT(ERROR, "L2CAP COC connect failed, rc=%d", rc);
os_mbuf_free_chain(sdu_rx); /* sdu_rx is freed by the stack on all failure paths; do not free here */
} }
} }
@@ -163,6 +156,7 @@ blecent_l2cap_coc_event_cb(struct ble_l2cap_event *event, void *arg)
case BLE_L2CAP_EVENT_COC_DISCONNECTED: case BLE_L2CAP_EVENT_COC_DISCONNECTED:
console_printf("LE CoC disconnected, chan: %p\n", console_printf("LE CoC disconnected, chan: %p\n",
event->disconnect.chan); event->disconnect.chan);
coc_chan = NULL;
return 0; return 0;
default: default:
@@ -266,8 +260,7 @@ ext_blecent_should_connect(const struct ble_gap_ext_disc_desc *disc)
int offset = 0; int offset = 0;
int ad_struct_len = 0; int ad_struct_len = 0;
uint8_t test_addr[6]; uint8_t test_addr[6];
if (disc->legacy_event_type != BLE_HCI_ADV_RPT_EVTYPE_ADV_IND && if (!(disc->props & BLE_HCI_ADV_CONN_MASK)) {
disc->legacy_event_type != BLE_HCI_ADV_RPT_EVTYPE_DIR_IND) {
return 0; return 0;
} }
if (strlen(CONFIG_EXAMPLE_PEER_ADDR) && if (strlen(CONFIG_EXAMPLE_PEER_ADDR) &&
@@ -296,11 +289,11 @@ ext_blecent_should_connect(const struct ble_gap_ext_disc_desc *disc)
break; break;
} }
/* AD Type (1) + UUID16 (2) requires at least 4 bytes total */ /* AD Type (1) + UUID16 (2) requires at least 4 bytes total; UUID 0x1812 is LE [0x12, 0x18] */
if (ad_struct_len >= 3 && if (ad_struct_len >= 3 &&
disc->data[offset + 1] == 0x03 && disc->data[offset + 1] == 0x03 &&
disc->data[offset + 2] == 0x18 && disc->data[offset + 2] == 0x12 &&
disc->data[offset + 3] == 0x12) { disc->data[offset + 3] == 0x18) {
return 1; return 1;
} }
@@ -386,6 +379,9 @@ blecent_connect_if_interesting(void *disc)
rc = ble_hs_id_infer_auto(0, &own_addr_type); rc = ble_hs_id_infer_auto(0, &own_addr_type);
if (rc != 0) { if (rc != 0) {
MODLOG_DFLT(ERROR, "error determining address type; rc=%d\n", rc); MODLOG_DFLT(ERROR, "error determining address type; rc=%d\n", rc);
#if !(MYNEWT_VAL(BLE_HOST_ALLOW_CONNECT_WITH_SCAN))
blecent_scan();
#endif
return; return;
} }
@@ -404,6 +400,9 @@ blecent_connect_if_interesting(void *disc)
MODLOG_DFLT(ERROR, "Error: Failed to connect to device; addr_type=%d " MODLOG_DFLT(ERROR, "Error: Failed to connect to device; addr_type=%d "
"addr=%s; rc=%d\n", "addr=%s; rc=%d\n",
addr->type, addr_str(addr->val), rc); addr->type, addr_str(addr->val), rc);
#if !(MYNEWT_VAL(BLE_HOST_ALLOW_CONNECT_WITH_SCAN))
blecent_scan();
#endif
return; return;
} }
} }
@@ -491,6 +490,10 @@ blecent_gap_event(struct ble_gap_event *event, void *arg)
print_conn_desc(&event->disconnect.conn); print_conn_desc(&event->disconnect.conn);
MODLOG_DFLT(INFO, "\n"); MODLOG_DFLT(INFO, "\n");
#if MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM) >= 1
coc_chan = NULL;
#endif
/* Forget about peer. */ /* Forget about peer. */
peer_delete(event->disconnect.conn.conn_handle); peer_delete(event->disconnect.conn.conn_handle);
@@ -536,6 +539,9 @@ static void
blecent_on_reset(int reason) blecent_on_reset(int reason)
{ {
MODLOG_DFLT(ERROR, "Resetting state; reason=%d\n", reason); MODLOG_DFLT(ERROR, "Resetting state; reason=%d\n", reason);
#if MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM) >= 1
coc_chan = NULL;
#endif
} }
static void static void
@@ -18,7 +18,7 @@
static uint8_t ext_adv_pattern_1[] = { static uint8_t ext_adv_pattern_1[] = {
0x02, BLE_HS_ADV_TYPE_FLAGS, 0x06, 0x02, BLE_HS_ADV_TYPE_FLAGS, 0x06,
0x03, BLE_HS_ADV_TYPE_COMP_UUIDS16, 0xab, 0xcd, 0x03, BLE_HS_ADV_TYPE_COMP_UUIDS16, 0xab, 0xcd,
0x03, BLE_HS_ADV_TYPE_COMP_UUIDS16, 0x18, 0x12, 0x03, BLE_HS_ADV_TYPE_COMP_UUIDS16, 0x12, 0x18, /* UUID 0x1812 in little-endian */
0x12, BLE_HS_ADV_TYPE_COMP_NAME, 'e', 'x', 't', '-', 'b', 'l', 'e', 'p', 'r', 'p', 'h', '-', 'l', '2', 'c', 'o', 'c', 0x12, BLE_HS_ADV_TYPE_COMP_NAME, 'e', 'x', 't', '-', 'b', 'l', 'e', 'p', 'r', 'p', 'h', '-', 'l', '2', 'c', 'o', 'c',
}; };
#endif #endif
@@ -164,9 +164,10 @@ bleprph_advertise(void)
fields.name_len = strlen(name); fields.name_len = strlen(name);
fields.name_is_complete = 1; fields.name_is_complete = 1;
fields.uuids16 = (ble_uuid16_t[]) { static const ble_uuid16_t adv_uuids16[] = {
BLE_UUID16_INIT(L2CAP_COC_UUID) BLE_UUID16_INIT(L2CAP_COC_UUID)
}; };
fields.uuids16 = adv_uuids16;
fields.num_uuids16 = 1; fields.num_uuids16 = 1;
fields.uuids16_is_complete = 1; fields.uuids16_is_complete = 1;
@@ -259,7 +260,7 @@ bleprph_l2cap_coc_event_cb(struct ble_l2cap_event *event, void *arg)
for (int i = 0; i < event->receive.sdu_rx->om_len; i++) { for (int i = 0; i < event->receive.sdu_rx->om_len; i++) {
console_printf("%d ", event->receive.sdu_rx->om_data[i]); console_printf("%d ", event->receive.sdu_rx->om_data[i]);
} }
os_mbuf_free(event->receive.sdu_rx); os_mbuf_free_chain(event->receive.sdu_rx);
} }
fflush(stdout); fflush(stdout);
bleprph_l2cap_coc_accept(event->receive.conn_handle, bleprph_l2cap_coc_accept(event->receive.conn_handle,
@@ -109,6 +109,9 @@ gatt_svc_access(uint16_t conn_handle, uint16_t attr_handle,
case BLE_GATT_ACCESS_OP_WRITE_CHR: case BLE_GATT_ACCESS_OP_WRITE_CHR:
MODLOG_DFLT(INFO, "Characteristic write; conn_handle=%d attr_handle=%d", MODLOG_DFLT(INFO, "Characteristic write; conn_handle=%d attr_handle=%d",
conn_handle, attr_handle); conn_handle, attr_handle);
if (OS_MBUF_PKTLEN(ctxt->om) != sizeof(gatt_svr_chr_val)) {
return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
}
rc = os_mbuf_copydata(ctxt->om, rc = os_mbuf_copydata(ctxt->om,
0, 0,
sizeof(gatt_svr_chr_val), sizeof(gatt_svr_chr_val),
@@ -374,7 +374,7 @@ ble_multi_adv_gap_event(struct ble_gap_event *event, void *arg)
assert(rc == 0); assert(rc == 0);
ble_multi_adv_print_conn_desc(&desc); ble_multi_adv_print_conn_desc(&desc);
ble_multi_perform_gatt_proc(desc.our_id_addr); ble_multi_perform_gatt_proc(desc.our_ota_addr);
} }
MODLOG_DFLT(INFO, "\n"); MODLOG_DFLT(INFO, "\n");
return 0; return 0;
@@ -102,18 +102,21 @@ ble_cent_client_gap_event(struct ble_gap_event *event, void *arg)
case BLE_GAP_EVENT_CONNECT: case BLE_GAP_EVENT_CONNECT:
if (event->connect.status == 0) { if (event->connect.status == 0) {
s_ble_multi_conn_num++;
ESP_LOGI(TAG, "Connection established. Handle:%d, Total:%d", event->connect.conn_handle, ESP_LOGI(TAG, "Connection established. Handle:%d, Total:%d", event->connect.conn_handle,
++s_ble_multi_conn_num); s_ble_multi_conn_num);
/* Remember peer. */ /* Remember peer. */
rc = peer_add(event->connect.conn_handle); rc = peer_add(event->connect.conn_handle);
if (rc != 0) { if (rc != 0) {
ESP_LOGE(TAG, "Failed to add peer; rc=%d\n", rc); ESP_LOGE(TAG, "Failed to add peer; rc=%d\n", rc);
ble_gap_terminate(event->connect.conn_handle, BLE_ERR_REM_USER_CONN_TERM);
} else { } else {
/* Perform service discovery */ /* Perform service discovery */
rc = peer_disc_svc_by_uuid(event->connect.conn_handle, remote_svc_uuid, rc = peer_disc_svc_by_uuid(event->connect.conn_handle, remote_svc_uuid,
ble_cent_on_disc_complete, NULL); ble_cent_on_disc_complete, NULL);
if(rc != 0) { if(rc != 0) {
ESP_LOGE(TAG, "Failed to discover services; rc=%d\n", rc); ESP_LOGE(TAG, "Failed to discover services; rc=%d\n", rc);
ble_gap_terminate(event->connect.conn_handle, BLE_ERR_REM_USER_CONN_TERM);
} }
} }
} else { } else {
@@ -130,9 +133,10 @@ ble_cent_client_gap_event(struct ble_gap_event *event, void *arg)
/* Forget about peer. */ /* Forget about peer. */
peer_delete(event->disconnect.conn.conn_handle); peer_delete(event->disconnect.conn.conn_handle);
s_ble_multi_conn_num--;
ESP_LOGI(TAG, "Central disconnected; Handle:%d, Reason=%d, Total:%d", ESP_LOGI(TAG, "Central disconnected; Handle:%d, Reason=%d, Total:%d",
event->disconnect.conn.conn_handle, event->disconnect.reason, event->disconnect.conn.conn_handle, event->disconnect.reason,
--s_ble_multi_conn_num); s_ble_multi_conn_num);
/* Resume scanning. */ /* Resume scanning. */
ble_cent_scan(); ble_cent_scan();
@@ -282,8 +286,8 @@ ble_cent_scan(void)
return; return;
} }
struct ble_gap_ext_disc_params uncoded_disc_params; struct ble_gap_ext_disc_params uncoded_disc_params = {0};
struct ble_gap_ext_disc_params coded_disc_params; struct ble_gap_ext_disc_params coded_disc_params = {0};
/* Perform a passive scan. I.e., don't send follow-up scan requests to /* Perform a passive scan. I.e., don't send follow-up scan requests to
* each advertiser. * each advertiser.
@@ -409,8 +413,10 @@ blecent_on_sync(void)
* the `MINIMUM_CONN_INTERVAL` should be greater than ((261 * 8us) * 2 + 150us) * 10 = 43260us. * the `MINIMUM_CONN_INTERVAL` should be greater than ((261 * 8us) * 2 + 150us) * 10 = 43260us.
* *
*/ */
rc = ble_gap_common_factor_set(true, (BLE_PREF_CONN_ITVL_MS * 1000) / 625); if (BLE_PEER_MAX_NUM > 0) {
assert(rc == 0); rc = ble_gap_common_factor_set(true, (BLE_PREF_CONN_ITVL_MS * 1000) / 625);
assert(rc == 0);
}
/* Make sure we have proper identity address set (public preferred) */ /* Make sure we have proper identity address set (public preferred) */
rc = ble_hs_util_ensure_addr(0); rc = ble_hs_util_ensure_addr(0);
@@ -24,7 +24,7 @@ static uint8_t ext_adv_pattern_1[] = {
#endif #endif
static const char *TAG = "ESP_MULTI_CONN_PRPH"; static const char *TAG = "ESP_MULTI_CONN_PRPH";
static uint8_t s_ble_prph_conn_num = 0; static volatile uint8_t s_ble_prph_conn_num = 0;
static SemaphoreHandle_t s_sem_restart_adv = NULL; static SemaphoreHandle_t s_sem_restart_adv = NULL;
static int ble_prph_gap_event(struct ble_gap_event *event, void *arg); static int ble_prph_gap_event(struct ble_gap_event *event, void *arg);
@@ -194,8 +194,9 @@ ble_prph_gap_event(struct ble_gap_event *event, void *arg)
case BLE_GAP_EVENT_CONNECT: case BLE_GAP_EVENT_CONNECT:
if (event->connect.status == 0) { if (event->connect.status == 0) {
/* A new connection was established. */ /* A new connection was established. */
s_ble_prph_conn_num++;
ESP_LOGI(TAG, "Connection established. Handle:%d. Total:%d", event->connect.conn_handle, ESP_LOGI(TAG, "Connection established. Handle:%d. Total:%d", event->connect.conn_handle,
++s_ble_prph_conn_num); s_ble_prph_conn_num);
#if CONFIG_EXAMPLE_RESTART_ADV_AFTER_CONNECTED #if CONFIG_EXAMPLE_RESTART_ADV_AFTER_CONNECTED
ble_prph_restart_adv(); ble_prph_restart_adv();
#endif //CONFIG_EXAMPLE_RESTART_ADV_AFTER_CONNECTED #endif //CONFIG_EXAMPLE_RESTART_ADV_AFTER_CONNECTED
@@ -206,8 +207,9 @@ ble_prph_gap_event(struct ble_gap_event *event, void *arg)
return 0; return 0;
case BLE_GAP_EVENT_DISCONNECT: case BLE_GAP_EVENT_DISCONNECT:
s_ble_prph_conn_num--;
ESP_LOGI(TAG, "Disconnect. Handle:%d. Reason=%d. Total:%d", ESP_LOGI(TAG, "Disconnect. Handle:%d. Reason=%d. Total:%d",
event->disconnect.conn.conn_handle, event->disconnect.reason, --s_ble_prph_conn_num); event->disconnect.conn.conn_handle, event->disconnect.reason, s_ble_prph_conn_num);
/* Connection terminated; resume advertising. */ /* Connection terminated; resume advertising. */
ble_prph_restart_adv(); ble_prph_restart_adv();
@@ -42,11 +42,20 @@ gap_event_cb(struct ble_gap_event *event, void *arg)
event->periodic_adv_subev_data_req.subevent_data_count); event->periodic_adv_subev_data_req.subevent_data_count);
sent_num = event->periodic_adv_subev_data_req.subevent_data_count; sent_num = event->periodic_adv_subev_data_req.subevent_data_count;
if (sent_num > BLE_PAWR_NUM_SUBEVTS) {
ESP_LOGE(TAG, "subevent_data_count %d exceeds max %d", sent_num, BLE_PAWR_NUM_SUBEVTS);
sent_num = BLE_PAWR_NUM_SUBEVTS;
}
uint8_t actual_sent = 0;
for (uint8_t i = 0; i < sent_num; i++) { for (uint8_t i = 0; i < sent_num; i++) {
data = os_msys_get_pkthdr(BLE_PAWR_SUB_DATA_LEN, 0); data = os_msys_get_pkthdr(BLE_PAWR_SUB_DATA_LEN, 0);
if (!data) { if (!data) {
ESP_LOGE(TAG, "No memory, %d", i); ESP_LOGE(TAG, "No memory, %d", i);
break; for (uint8_t j = 0; j < i; j++) {
os_mbuf_free_chain(sub_data_params[j].data);
sub_data_params[j].data = NULL;
}
return 0;
} }
sub = (i + event->periodic_adv_subev_data_req.subevent_start) % BLE_PAWR_NUM_SUBEVTS; sub = (i + event->periodic_adv_subev_data_req.subevent_start) % BLE_PAWR_NUM_SUBEVTS;
memset(&sub_data_pattern[1], sub, BLE_PAWR_SUB_DATA_LEN - 1); memset(&sub_data_pattern[1], sub, BLE_PAWR_SUB_DATA_LEN - 1);
@@ -56,10 +65,11 @@ gap_event_cb(struct ble_gap_event *event, void *arg)
sub_data_params[i].response_slot_count = BLE_PAWR_NUM_RSP_SLOTS; sub_data_params[i].response_slot_count = BLE_PAWR_NUM_RSP_SLOTS;
sub_data_params[i].data = data; sub_data_params[i].data = data;
sub_data_pattern[0]++; sub_data_pattern[0]++;
actual_sent++;
} }
rc = ble_gap_set_periodic_adv_subev_data(event->periodic_adv_subev_data_req.adv_handle, rc = ble_gap_set_periodic_adv_subev_data(event->periodic_adv_subev_data_req.adv_handle,
sent_num, sub_data_params); actual_sent, sub_data_params);
if (rc) { if (rc) {
ESP_LOGE(TAG, "Failed to set Subevent Data, rc = 0x%x", rc); ESP_LOGE(TAG, "Failed to set Subevent Data, rc = 0x%x", rc);
} }
@@ -72,7 +82,9 @@ gap_event_cb(struct ble_gap_event *event, void *arg)
event->periodic_adv_response.response_slot, event->periodic_adv_response.response_slot,
event->periodic_adv_response.data_length); event->periodic_adv_response.data_length);
const uint8_t *data = event->periodic_adv_response.data; const uint8_t *data = event->periodic_adv_response.data;
ESP_LOGI(TAG, "data: 0x%0x, 0x%0x", data[0], data[1]); if (data != NULL && event->periodic_adv_response.data_length >= 2) {
ESP_LOGI(TAG, "data: 0x%0x, 0x%0x", data[0], data[1]);
}
} else { } else {
ESP_LOGE(TAG, "[Response] subevent:%d, response_slot:%d, rsp_data status:%d", ESP_LOGE(TAG, "[Response] subevent:%d, response_slot:%d, rsp_data status:%d",
event->periodic_adv_response.subevent, event->periodic_adv_response.subevent,
@@ -102,8 +114,7 @@ start_periodic_adv(uint8_t own_addr_type)
#endif #endif
/* Get the local address. */ /* Get the local address. */
uint8_t addr_type = own_addr_type == BLE_OWN_ADDR_RANDOM ? BLE_ADDR_RANDOM : BLE_ADDR_PUBLIC; rc = ble_hs_id_copy_addr(own_addr_type, addr, NULL);
rc = ble_hs_id_copy_addr(addr_type, addr, NULL);
assert (rc == 0); assert (rc == 0);
ESP_LOGI(TAG, "Device Address %02x:%02x:%02x:%02x:%02x:%02x", addr[5], addr[4], addr[3], ESP_LOGI(TAG, "Device Address %02x:%02x:%02x:%02x:%02x:%02x", addr[5], addr[4], addr[3],
@@ -47,7 +47,7 @@ gap_event_cb(struct ble_gap_event *event, void *arg)
return 0; return 0;
} }
if (disc->periodic_adv_itvl && fields.name_len && !memcmp(fields.name, TARGET_NAME, strlen(TARGET_NAME))) { if (disc->periodic_adv_itvl && fields.name_len == strlen(TARGET_NAME) && !memcmp(fields.name, TARGET_NAME, fields.name_len)) {
create_periodic_sync(disc); create_periodic_sync(disc);
} }
return 0; return 0;
@@ -71,6 +71,10 @@ gap_event_cb(struct ble_gap_event *event, void *arg)
return 0; return 0;
} }
// create a special data for checking manually in ADV side // create a special data for checking manually in ADV side
if (event->periodic_report.data == NULL || event->periodic_report.data_length == 0) {
os_mbuf_free_chain(data);
return 0;
}
sub_data_pattern[0] = event->periodic_report.data[0]; sub_data_pattern[0] = event->periodic_report.data[0];
memset(sub_data_pattern + 1, event->periodic_report.subevent, BLE_PAWR_RSP_DATA_LEN - 1); memset(sub_data_pattern + 1, event->periodic_report.subevent, BLE_PAWR_RSP_DATA_LEN - 1);
os_mbuf_append(data, sub_data_pattern, BLE_PAWR_RSP_DATA_LEN); os_mbuf_append(data, sub_data_pattern, BLE_PAWR_RSP_DATA_LEN);
@@ -190,6 +194,7 @@ static void
on_reset(int reason) on_reset(int reason)
{ {
ESP_LOGE(TAG, "Resetting state; reason=%d\n", reason); ESP_LOGE(TAG, "Resetting state; reason=%d\n", reason);
synced = false;
} }
static void static void
@@ -147,6 +147,10 @@ gap_event_cb(struct ble_gap_event *event, void *arg)
event->periodic_adv_response.response_slot, event->periodic_adv_response.response_slot,
event->periodic_adv_response.data_length); event->periodic_adv_response.data_length);
const uint8_t *data = event->periodic_adv_response.data; const uint8_t *data = event->periodic_adv_response.data;
if (data == NULL || event->periodic_adv_response.data_length < 10) {
ESP_LOGE(TAG, "Invalid response data: NULL or too short (%d)", event->periodic_adv_response.data_length);
return 0;
}
ESP_LOGI(TAG, "data: 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x", ESP_LOGI(TAG, "data: 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x",
data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7], data[8], data[9]); data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7], data[8], data[9]);
@@ -161,10 +165,10 @@ gap_event_cb(struct ble_gap_event *event, void *arg)
rc = ble_gap_connect_with_synced(own_addr_type,adv_handle,subevent,&peer_addr,30000,phy_mask,NULL,NULL,NULL,gap_event_cb,NULL); rc = ble_gap_connect_with_synced(own_addr_type,adv_handle,subevent,&peer_addr,30000,phy_mask,NULL,NULL,NULL,gap_event_cb,NULL);
if (rc != 0 ) { if (rc != 0 ) {
ESP_LOGI(TAG,"Error: Failed to connect to device , rc = %d\n",rc); ESP_LOGI(TAG,"Error: Failed to connect to device , rc = %d\n",rc);
}else { } else {
ESP_LOGI(TAG,"Connection create sent, adv handle = %d, subevent = %d", adv_handle, subevent); ESP_LOGI(TAG,"Connection create sent, adv handle = %d, subevent = %d", adv_handle, subevent);
conn = 1;
} }
conn = 1;
} }
} else { } else {
ESP_LOGE(TAG, "[Response] subevent:%d, response_slot:%d, rsp_data status:%d", ESP_LOGE(TAG, "[Response] subevent:%d, response_slot:%d, rsp_data status:%d",
@@ -265,6 +269,7 @@ static void
on_reset(int reason) on_reset(int reason)
{ {
ESP_LOGE(TAG, "Resetting state; reason=%d\n", reason); ESP_LOGE(TAG, "Resetting state; reason=%d\n", reason);
conn = 0;
} }
static void static void
@@ -100,7 +100,7 @@ gap_event_cb(struct ble_gap_event *event, void *arg)
return 0; return 0;
} }
if (fields.name_len && !memcmp(fields.name, TARGET_NAME, strlen(TARGET_NAME))) { if (fields.name_len == strlen(TARGET_NAME) && !memcmp(fields.name, TARGET_NAME, fields.name_len)) {
create_periodic_sync(disc); create_periodic_sync(disc);
} }
return 0; return 0;
@@ -229,6 +229,11 @@ start_scan(void)
int rc; int rc;
struct ble_gap_ext_disc_params disc_params; struct ble_gap_ext_disc_params disc_params;
/* Cancel any active scan first to reset the duplicate filter. */
if (ble_gap_disc_active()) {
ble_gap_disc_cancel();
}
/* Perform a passive scan. I.e., don't send follow-up scan requests to /* Perform a passive scan. I.e., don't send follow-up scan requests to
* each advertiser. * each advertiser.
*/ */
@@ -258,6 +263,7 @@ static void
on_reset(int reason) on_reset(int reason)
{ {
ESP_LOGE(TAG, "Resetting state; reason=%d\n", reason); ESP_LOGE(TAG, "Resetting state; reason=%d\n", reason);
synced = false;
} }
static void static void
@@ -106,7 +106,6 @@ periodic_sync_gap_event(struct ble_gap_event *event, void *arg)
if (disc->sid == 2 && synced == 0) { if (disc->sid == 2 && synced == 0) {
struct ble_gap_periodic_sync_params params = {0}; struct ble_gap_periodic_sync_params params = {0};
int rc; int rc;
synced++;
params.skip = 10; params.skip = 10;
params.sync_timeout = 1000; params.sync_timeout = 1000;
@@ -117,7 +116,11 @@ periodic_sync_gap_event(struct ble_gap_event *event, void *arg)
params.filter_duplicates = 1; params.filter_duplicates = 1;
#endif #endif
rc = ble_gap_periodic_adv_sync_create(&disc->addr, disc->sid, &params, periodic_sync_gap_event, NULL); rc = ble_gap_periodic_adv_sync_create(&disc->addr, disc->sid, &params, periodic_sync_gap_event, NULL);
assert(rc == 0); if (rc != 0) {
MODLOG_DFLT(ERROR, "Failed to create periodic sync; rc=%d\n", rc);
} else {
synced++;
}
} }
return 0; return 0;
} }
@@ -151,6 +154,7 @@ static void
periodic_sync_on_reset(int reason) periodic_sync_on_reset(int reason)
{ {
MODLOG_DFLT(ERROR, "Resetting state; reason=%d\n", reason); MODLOG_DFLT(ERROR, "Resetting state; reason=%d\n", reason);
synced = 0;
} }
static void static void
@@ -66,17 +66,28 @@ blecent_on_read(uint16_t conn_handle,
MODLOG_DFLT(INFO, " attr_handle=%d value=", attr->handle); MODLOG_DFLT(INFO, " attr_handle=%d value=", attr->handle);
print_mbuf(attr->om); print_mbuf(attr->om);
} }
MODLOG_DFLT(INFO, "\n"); MODLOG_DFLT(INFO, "\n");
if (error->status != 0) {
MODLOG_DFLT(ERROR, "Read failed; terminating connection\n");
ble_gap_terminate(conn_handle, BLE_ERR_REM_USER_CONN_TERM);
return 0;
}
/* Write 1000 bytes to the LE PHY characteristic.*/ /* Write 1000 bytes to the LE PHY characteristic.*/
const struct peer_chr *chr; const struct peer_chr *chr;
int len = 1000; const int len = 1000;
uint8_t value[len]; uint8_t *value;
int rc; int rc;
struct os_mbuf *txom; struct os_mbuf *txom;
const struct peer *peer = peer_find(conn_handle); const struct peer *peer = peer_find(conn_handle);
if (peer == NULL) {
MODLOG_DFLT(ERROR, "Error: peer not found for conn_handle=%d\n", conn_handle);
ble_gap_terminate(conn_handle, BLE_ERR_REM_USER_CONN_TERM);
return 0;
}
chr = peer_chr_find_uuid(peer, chr = peer_chr_find_uuid(peer,
BLE_UUID16_DECLARE(LE_PHY_UUID16), BLE_UUID16_DECLARE(LE_PHY_UUID16),
BLE_UUID16_DECLARE(LE_PHY_CHR_UUID16)); BLE_UUID16_DECLARE(LE_PHY_CHR_UUID16));
@@ -86,12 +97,19 @@ blecent_on_read(uint16_t conn_handle,
goto err; goto err;
} }
value = malloc(len);
if (value == NULL) {
MODLOG_DFLT(ERROR, "Insufficient memory\n");
goto err;
}
/* Fill the value array with data */ /* Fill the value array with data */
for (int i = 0; i < len; i++) { for (int i = 0; i < len; i++) {
value[i] = i; value[i] = i;
} }
txom = ble_hs_mbuf_from_flat(&value, len); txom = ble_hs_mbuf_from_flat(value, len);
free(value);
if (!txom) { if (!txom) {
MODLOG_DFLT(ERROR, "Insufficient memory"); MODLOG_DFLT(ERROR, "Insufficient memory");
goto err; goto err;
@@ -108,7 +126,7 @@ blecent_on_read(uint16_t conn_handle,
err: err:
/* Terminate the connection. */ /* Terminate the connection. */
return ble_gap_terminate(peer->conn_handle, BLE_ERR_REM_USER_CONN_TERM); return ble_gap_terminate(conn_handle, BLE_ERR_REM_USER_CONN_TERM);
} }
static void static void
@@ -234,18 +252,15 @@ ext_blecent_should_connect(const struct ble_gap_ext_disc_desc *disc)
{ {
int offset = 0; int offset = 0;
int ad_struct_len = 0; int ad_struct_len = 0;
uint8_t test_addr[6]; uint8_t test_addr[6] = {0};
if (disc->legacy_event_type != BLE_HCI_ADV_RPT_EVTYPE_ADV_IND && if (!(disc->props & BLE_HCI_ADV_CONN_MASK)) {
disc->legacy_event_type != BLE_HCI_ADV_RPT_EVTYPE_DIR_IND) {
return 0; return 0;
} }
if (strlen(CONFIG_EXAMPLE_PEER_ADDR) && (strncmp(CONFIG_EXAMPLE_PEER_ADDR, "ADDR_ANY", strlen("ADDR_ANY")) != 0)) { if (strlen(CONFIG_EXAMPLE_PEER_ADDR) && (strncmp(CONFIG_EXAMPLE_PEER_ADDR, "ADDR_ANY", strlen("ADDR_ANY")) != 0)) {
ESP_LOGI(tag, "Peer address from menuconfig: %s", CONFIG_EXAMPLE_PEER_ADDR); ESP_LOGI(tag, "Peer address from menuconfig: %s", CONFIG_EXAMPLE_PEER_ADDR);
/* Convert string to address */ /* Convert string to address */
uint8_t parsed_addr[6]; if (peer_addr_parse(CONFIG_EXAMPLE_PEER_ADDR, test_addr) != 6) {
peer_addr_parse(CONFIG_EXAMPLE_PEER_ADDR, parsed_addr); return 0;
for (int i = 0; i < 6; i++) {
test_addr[5 - i] = parsed_addr[i];
} }
if (memcmp(test_addr, disc->addr.val, sizeof(disc->addr.val)) != 0) { if (memcmp(test_addr, disc->addr.val, sizeof(disc->addr.val)) != 0) {
@@ -255,19 +270,24 @@ ext_blecent_should_connect(const struct ble_gap_ext_disc_desc *disc)
/* The device has to advertise support LE PHY UUID (0xABF2). /* The device has to advertise support LE PHY UUID (0xABF2).
*/ */
do { while (offset < disc->length_data) {
if (offset + 1 >= disc->length_data) {
break;
}
ad_struct_len = disc->data[offset]; ad_struct_len = disc->data[offset];
if (!ad_struct_len || (offset + ad_struct_len + 1 > disc->length_data)) { if (!ad_struct_len || (offset + ad_struct_len + 1 > disc->length_data)) {
break; break;
} }
/* Search if LE PHY UUID is advertised */ /* Search if LE PHY UUID is advertised (UUID list, type 0x02 or 0x03) */
if (disc->data[offset] == 0x03 && disc->data[offset + 1] == 0x03) { if (ad_struct_len >= 3 && (disc->data[offset + 1] == 0x02 || disc->data[offset + 1] == 0x03)) {
if ( disc->data[offset + 2] == 0xAB && disc->data[offset + 3] == 0xF2 ) { for (int i = 2; i + 1 <= ad_struct_len; i += 2) {
return 1; if (disc->data[offset + i] == 0xF2 && disc->data[offset + i + 1] == 0xAB) {
return 1;
}
} }
} }
offset += ad_struct_len + 1; offset += ad_struct_len + 1;
} while (offset < disc->length_data); }
return 0; return 0;
} }
@@ -301,6 +321,9 @@ blecent_connect_if_interesting(void *disc)
rc = ble_hs_id_infer_auto(0, &own_addr_type); rc = ble_hs_id_infer_auto(0, &own_addr_type);
if (rc != 0) { if (rc != 0) {
MODLOG_DFLT(ERROR, "error determining address type; rc=%d\n", rc); MODLOG_DFLT(ERROR, "error determining address type; rc=%d\n", rc);
#if !(MYNEWT_VAL(BLE_HOST_ALLOW_CONNECT_WITH_SCAN))
blecent_scan();
#endif
return; return;
} }
@@ -322,6 +345,9 @@ blecent_connect_if_interesting(void *disc)
MODLOG_DFLT(ERROR, "Error: Failed to connect to device; addr_type=%d " MODLOG_DFLT(ERROR, "Error: Failed to connect to device; addr_type=%d "
"addr=%s; rc=%d\n", "addr=%s; rc=%d\n",
conn_addr.type, addr_str(conn_addr.val), rc); conn_addr.type, addr_str(conn_addr.val), rc);
#if !(MYNEWT_VAL(BLE_HOST_ALLOW_CONNECT_WITH_SCAN))
blecent_scan();
#endif
return; return;
} }
} }
@@ -344,6 +370,7 @@ static int
blecent_gap_event(struct ble_gap_event *event, void *arg) blecent_gap_event(struct ble_gap_event *event, void *arg)
{ {
struct ble_gap_conn_desc desc; struct ble_gap_conn_desc desc;
uint8_t own_addr_type;
int rc; int rc;
switch (event->type) { switch (event->type) {
@@ -419,20 +446,36 @@ blecent_gap_event(struct ble_gap_event *event, void *arg)
case BLE_HCI_LE_PHY_CODED_PREF_MASK: case BLE_HCI_LE_PHY_CODED_PREF_MASK:
return 0; return 0;
default:
return 0;
} }
vTaskDelay(200); vTaskDelay(200);
rc = ble_hs_id_infer_auto(0, &own_addr_type);
if (rc != 0) {
MODLOG_DFLT(ERROR, "error determining address type; rc=%d\n", rc);
return 0;
}
/* Attempt direct connection on 2M or Coded phy now */ /* Attempt direct connection on 2M or Coded phy now */
if (s_current_phy == BLE_HCI_LE_PHY_CODED_PREF_MASK) { if (s_current_phy == BLE_HCI_LE_PHY_CODED_PREF_MASK) {
MODLOG_DFLT(INFO, " Attempting to initiate connection on Coded PHY \n"); MODLOG_DFLT(INFO, " Attempting to initiate connection on Coded PHY \n");
ble_gap_ext_connect(0, &conn_addr, 30000, BLE_HCI_LE_PHY_CODED_PREF_MASK, rc = ble_gap_ext_connect(own_addr_type, &conn_addr, 30000, BLE_HCI_LE_PHY_CODED_PREF_MASK,
NULL, NULL, NULL, blecent_gap_event, NULL); NULL, NULL, NULL, blecent_gap_event, NULL);
if (rc != 0) {
MODLOG_DFLT(ERROR, "Error: Failed to initiate ext connect; rc=%d\n", rc);
}
} }
else if (s_current_phy == BLE_HCI_LE_PHY_2M_PREF_MASK) { else if (s_current_phy == BLE_HCI_LE_PHY_2M_PREF_MASK) {
MODLOG_DFLT(INFO, " Attempting to initiate connection on 2M PHY \n"); MODLOG_DFLT(INFO, " Attempting to initiate connection on 2M PHY \n");
ble_gap_ext_connect(0, &conn_addr, 30000, (BLE_HCI_LE_PHY_1M_PREF_MASK | BLE_HCI_LE_PHY_2M_PREF_MASK), rc = ble_gap_ext_connect(own_addr_type, &conn_addr, 30000,
NULL, NULL, NULL, blecent_gap_event, NULL); (BLE_HCI_LE_PHY_1M_PREF_MASK | BLE_HCI_LE_PHY_2M_PREF_MASK),
NULL, NULL, NULL, blecent_gap_event, NULL);
if (rc != 0) {
MODLOG_DFLT(ERROR, "Error: Failed to initiate ext connect; rc=%d\n", rc);
}
} }
return 0; return 0;
@@ -464,11 +507,18 @@ blecent_on_sync(void)
{ {
int ii, rc; int ii, rc;
uint8_t all_phy; uint8_t all_phy;
uint8_t own_addr_type;
uint8_t test_addr[6]; uint8_t test_addr[6];
/* Make sure we have proper identity address set (public preferred) */ /* Make sure we have proper identity address set (public preferred) */
rc = ble_hs_util_ensure_addr(0); rc = ble_hs_util_ensure_addr(0);
assert(rc == 0); assert(rc == 0);
rc = ble_hs_id_infer_auto(0, &own_addr_type);
if (rc != 0) {
MODLOG_DFLT(ERROR, "error determining address type; rc=%d\n", rc);
return;
}
all_phy = BLE_HCI_LE_PHY_1M_PREF_MASK | BLE_HCI_LE_PHY_2M_PREF_MASK | BLE_HCI_LE_PHY_CODED_PREF_MASK; all_phy = BLE_HCI_LE_PHY_1M_PREF_MASK | BLE_HCI_LE_PHY_2M_PREF_MASK | BLE_HCI_LE_PHY_CODED_PREF_MASK;
set_default_le_phy(all_phy, all_phy); set_default_le_phy(all_phy, all_phy);
@@ -484,10 +534,14 @@ blecent_on_sync(void)
vTaskDelay(300); vTaskDelay(300);
s_current_phy = BLE_HCI_LE_PHY_1M_PREF_MASK | BLE_HCI_LE_PHY_2M_PREF_MASK ; s_current_phy = BLE_HCI_LE_PHY_2M_PREF_MASK;
ble_gap_ext_connect(0, &conn_addr, 30000, s_current_phy, rc = ble_gap_ext_connect(own_addr_type, &conn_addr, 30000,
NULL, NULL, NULL, blecent_gap_event, NULL); BLE_HCI_LE_PHY_1M_PREF_MASK | s_current_phy,
NULL, NULL, NULL, blecent_gap_event, NULL);
if (rc != 0) {
MODLOG_DFLT(ERROR, "Error: Failed to initiate ext connect; rc=%d\n", rc);
}
} }
else { else {
s_current_phy = BLE_HCI_LE_PHY_1M_PREF_MASK; s_current_phy = BLE_HCI_LE_PHY_1M_PREF_MASK;
@@ -531,11 +585,14 @@ app_main(void)
/* Initialize data structures to track connected peers. */ /* Initialize data structures to track connected peers. */
#if MYNEWT_VAL(BLE_INCL_SVC_DISCOVERY) || MYNEWT_VAL(BLE_GATT_CACHING_INCLUDE_SERVICES) #if MYNEWT_VAL(BLE_INCL_SVC_DISCOVERY) || MYNEWT_VAL(BLE_GATT_CACHING_INCLUDE_SERVICES)
rc = peer_init(MYNEWT_VAL(BLE_MAX_CONNECTIONS), 64, 64, 64, 64); rc = peer_init(MYNEWT_VAL(BLE_MAX_CONNECTIONS), 64, 64, 64, 64);
assert(rc == 0);
#else #else
rc = peer_init(MYNEWT_VAL(BLE_MAX_CONNECTIONS), 64, 64, 64); rc = peer_init(MYNEWT_VAL(BLE_MAX_CONNECTIONS), 64, 64, 64);
assert(rc == 0);
#endif #endif
if (rc != 0) {
MODLOG_DFLT(ERROR, "Failed to init peer; rc=%d\n", rc);
nimble_port_deinit();
return;
}
#if CONFIG_BT_NIMBLE_GAP_SERVICE #if CONFIG_BT_NIMBLE_GAP_SERVICE
/* Set the default device name. */ /* Set the default device name. */
rc = ble_svc_gap_device_name_set("blecent-phy"); rc = ble_svc_gap_device_name_set("blecent-phy");
@@ -31,8 +31,7 @@ static const struct ble_gatt_svc_def gatt_svr_svcs_le_phy[] = {
.uuid = BLE_UUID16_DECLARE(LE_PHY_CHR_UUID16), .uuid = BLE_UUID16_DECLARE(LE_PHY_CHR_UUID16),
.access_cb = gatt_svr_chr_access_le_phy, .access_cb = gatt_svr_chr_access_le_phy,
.val_handle = &gatt_svr_chr_val_handle, .val_handle = &gatt_svr_chr_val_handle,
.flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_READ_ENC | BLE_GATT_CHR_F_WRITE .flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_WRITE,
| BLE_GATT_CHR_F_WRITE_ENC,
}, { }, {
0, /* No more characteristics in this service. */ 0, /* No more characteristics in this service. */
} }
@@ -17,22 +17,19 @@
static uint8_t ext_adv_pattern_1M[] = { static uint8_t ext_adv_pattern_1M[] = {
0x02, BLE_HS_ADV_TYPE_FLAGS, 0x06, 0x02, BLE_HS_ADV_TYPE_FLAGS, 0x06,
0x03, BLE_HS_ADV_TYPE_COMP_UUIDS16, 0xab, 0xcd, 0x03, BLE_HS_ADV_TYPE_COMP_UUIDS16, 0xF2, 0xAB,
0x03, BLE_HS_ADV_TYPE_COMP_UUIDS16, 0xAB, 0xF2,
0x0e, BLE_HS_ADV_TYPE_COMP_NAME, 'b', 'l', 'e', 'p', 'r', 'p', 'h', '-', 'p', 'h', 'y', '-', '1', 'M', 0x0e, BLE_HS_ADV_TYPE_COMP_NAME, 'b', 'l', 'e', 'p', 'r', 'p', 'h', '-', 'p', 'h', 'y', '-', '1', 'M',
}; };
static uint8_t ext_adv_pattern_2M[] = { static uint8_t ext_adv_pattern_2M[] = {
0x02, BLE_HS_ADV_TYPE_FLAGS, 0x06, 0x02, BLE_HS_ADV_TYPE_FLAGS, 0x06,
0x03, BLE_HS_ADV_TYPE_COMP_UUIDS16, 0xab, 0xcd, 0x03, BLE_HS_ADV_TYPE_COMP_UUIDS16, 0xF2, 0xAB,
0x03, BLE_HS_ADV_TYPE_COMP_UUIDS16, 0xAB, 0xF2,
0x0e, BLE_HS_ADV_TYPE_COMP_NAME, 'b', 'l', 'e', 'p', 'r', 'p', 'h', '-', 'p', 'h', 'y', '-', '2', 'M', 0x0e, BLE_HS_ADV_TYPE_COMP_NAME, 'b', 'l', 'e', 'p', 'r', 'p', 'h', '-', 'p', 'h', 'y', '-', '2', 'M',
}; };
static uint8_t ext_adv_pattern_coded[] = { static uint8_t ext_adv_pattern_coded[] = {
0x02, BLE_HS_ADV_TYPE_FLAGS, 0x06, 0x02, BLE_HS_ADV_TYPE_FLAGS, 0x06,
0x03, BLE_HS_ADV_TYPE_COMP_UUIDS16, 0xab, 0xcd, 0x03, BLE_HS_ADV_TYPE_COMP_UUIDS16, 0xF2, 0xAB,
0x03, BLE_HS_ADV_TYPE_COMP_UUIDS16, 0xAB, 0xF2,
0x11, BLE_HS_ADV_TYPE_COMP_NAME, 'b', 'l', 'e', 'p', 'r', 'p', 'h', '-', 'p', 'h', 'y', '-', 'c', 'o', 'd', 'e', 0x11, BLE_HS_ADV_TYPE_COMP_NAME, 'b', 'l', 'e', 'p', 'r', 'p', 'h', '-', 'p', 'h', 'y', '-', 'c', 'o', 'd', 'e',
'd', 'd',
}; };
@@ -239,7 +236,10 @@ bleprph_gap_event(struct ble_gap_event *event, void *arg)
MODLOG_DFLT(INFO, "connection updated; status=%d ", MODLOG_DFLT(INFO, "connection updated; status=%d ",
event->conn_update.status); event->conn_update.status);
rc = ble_gap_conn_find(event->conn_update.conn_handle, &desc); rc = ble_gap_conn_find(event->conn_update.conn_handle, &desc);
assert(rc == 0); if (rc != 0) {
MODLOG_DFLT(ERROR, "ble_gap_conn_find failed; rc=%d\n", rc);
return 0;
}
bleprph_print_conn_desc(&desc); bleprph_print_conn_desc(&desc);
MODLOG_DFLT(INFO, "\n"); MODLOG_DFLT(INFO, "\n");
return 0; return 0;
@@ -352,9 +352,11 @@ app_main(void)
assert(rc == 0); assert(rc == 0);
#endif #endif
#if CONFIG_BT_NIMBLE_GAP_SERVICE
/* Set the default device name. */ /* Set the default device name. */
rc = ble_svc_gap_device_name_set("bleprph-phy"); rc = ble_svc_gap_device_name_set("bleprph-phy");
assert(rc == 0); assert(rc == 0);
#endif
/* XXX Need to have template for store */ /* XXX Need to have template for store */
ble_store_config_init(); ble_store_config_init();
@@ -12,3 +12,4 @@ CONFIG_BT_BLUEDROID_ENABLED=n
CONFIG_BT_NIMBLE_ENABLED=y CONFIG_BT_NIMBLE_ENABLED=y
CONFIG_BT_NIMBLE_EXT_ADV=y CONFIG_BT_NIMBLE_EXT_ADV=y
CONFIG_BT_NIMBLE_BLE_GATT_BLOB_TRANSFER=y CONFIG_BT_NIMBLE_BLE_GATT_BLOB_TRANSFER=y
CONFIG_BT_NIMBLE_MSYS_1_BLOCK_COUNT=64
@@ -19,7 +19,6 @@
#endif #endif
static const char *tag = "NimBLE_PROX_CENT"; static const char *tag = "NimBLE_PROX_CENT";
static uint8_t link_supervision_timeout;
static int8_t tx_pwr_lvl; static int8_t tx_pwr_lvl;
static struct ble_prox_cent_conn_peer conn_peer[MYNEWT_VAL(BLE_MAX_CONNECTIONS) + 1]; static struct ble_prox_cent_conn_peer conn_peer[MYNEWT_VAL(BLE_MAX_CONNECTIONS) + 1];
static struct ble_prox_cent_link_lost_peer disconn_peer[MYNEWT_VAL(BLE_MAX_CONNECTIONS) + 1]; static struct ble_prox_cent_link_lost_peer disconn_peer[MYNEWT_VAL(BLE_MAX_CONNECTIONS) + 1];
@@ -41,7 +40,11 @@ ble_prox_cent_on_read(uint16_t conn_handle,
if (error->status == 0) { if (error->status == 0) {
MODLOG_DFLT(INFO, " attr_handle=%d value=", attr->handle); MODLOG_DFLT(INFO, " attr_handle=%d value=", attr->handle);
print_mbuf(attr->om); print_mbuf(attr->om);
os_mbuf_copydata(attr->om, 0, attr->om->om_len, &tx_pwr_lvl); if (attr->om->om_len != sizeof(tx_pwr_lvl)) {
MODLOG_DFLT(ERROR, "Unexpected TX power level length: %d\n", attr->om->om_len);
return 0;
}
os_mbuf_copydata(attr->om, 0, sizeof(tx_pwr_lvl), &tx_pwr_lvl);
conn_peer[conn_handle].calc_path_loss = true; conn_peer[conn_handle].calc_path_loss = true;
} }
@@ -129,8 +132,10 @@ ble_prox_cent_read_write_subscribe(const struct peer *peer)
goto err; goto err;
} }
/* Alert Level: 0=No Alert, 1=Mild Alert, 2=High Alert (BLE SIG LLS spec) */
static const uint8_t alert_level = 1;
rc = ble_gattc_write_flat(peer->conn_handle, chr->chr.val_handle, rc = ble_gattc_write_flat(peer->conn_handle, chr->chr.val_handle,
&link_supervision_timeout, sizeof(link_supervision_timeout), &alert_level, sizeof(alert_level),
ble_prox_cent_on_write, NULL); ble_prox_cent_on_write, NULL);
if (rc != 0) { if (rc != 0) {
MODLOG_DFLT(ERROR, "Error: Failed to write characteristic; rc=%d\n", MODLOG_DFLT(ERROR, "Error: Failed to write characteristic; rc=%d\n",
@@ -228,19 +233,13 @@ ext_ble_prox_cent_should_connect(const struct ble_gap_ext_disc_desc *disc)
int offset = 0; int offset = 0;
int ad_struct_len = 0; int ad_struct_len = 0;
uint8_t test_addr[6]; uint8_t test_addr[6];
if (disc->legacy_event_type != BLE_HCI_ADV_RPT_EVTYPE_ADV_IND && if (!(disc->props & BLE_HCI_ADV_CONN_MASK)) {
disc->legacy_event_type != BLE_HCI_ADV_RPT_EVTYPE_DIR_IND) {
return 0; return 0;
} }
if (strlen(CONFIG_EXAMPLE_PEER_ADDR) && (strncmp(CONFIG_EXAMPLE_PEER_ADDR, "ADDR_ANY", strlen ("ADDR_ANY")) != 0)) { if (strlen(CONFIG_EXAMPLE_PEER_ADDR) && (strncmp(CONFIG_EXAMPLE_PEER_ADDR, "ADDR_ANY", strlen ("ADDR_ANY")) != 0)) {
ESP_LOGI(tag, "Peer address from menuconfig: %s", CONFIG_EXAMPLE_PEER_ADDR); ESP_LOGI(tag, "Peer address from menuconfig: %s", CONFIG_EXAMPLE_PEER_ADDR);
/* Convert string to address */ /* Convert string to address; peer_addr_parse outputs little-endian matching disc->addr.val */
uint8_t parsed_addr[6]; peer_addr_parse(CONFIG_EXAMPLE_PEER_ADDR, test_addr);
peer_addr_parse(CONFIG_EXAMPLE_PEER_ADDR, parsed_addr);
for (int i = 0; i < 6; i++) {
test_addr[5 - i] = parsed_addr[i];
}
if (memcmp(test_addr, disc->addr.val, sizeof(disc->addr.val)) != 0) { if (memcmp(test_addr, disc->addr.val, sizeof(disc->addr.val)) != 0) {
return 0; return 0;
} }
@@ -256,9 +255,9 @@ ext_ble_prox_cent_should_connect(const struct ble_gap_ext_disc_desc *disc)
break; break;
} }
/* Search if Proximity Sensor (Link loss) UUID is advertised */ /* Search if Proximity Sensor (Link loss) UUID 0x1803 is advertised (little-endian: [0x03, 0x18]) */
if (disc->data[offset] == 0x03 && disc->data[offset + 1] == 0x03) { if (disc->data[offset] == 0x03 && disc->data[offset + 1] == 0x03) {
if ( disc->data[offset + 2] == 0x18 && disc->data[offset + 3] == 0x03 ) { if ( disc->data[offset + 2] == 0x03 && disc->data[offset + 3] == 0x18 ) {
return 1; return 1;
} }
} }
@@ -347,6 +346,9 @@ ble_prox_cent_connect_if_interesting(void *disc)
rc = ble_hs_id_infer_auto(0, &own_addr_type); rc = ble_hs_id_infer_auto(0, &own_addr_type);
if (rc != 0) { if (rc != 0) {
MODLOG_DFLT(ERROR, "error determining address type; rc=%d\n", rc); MODLOG_DFLT(ERROR, "error determining address type; rc=%d\n", rc);
#if !(MYNEWT_VAL(BLE_HOST_ALLOW_CONNECT_WITH_SCAN))
ble_prox_cent_scan();
#endif
return; return;
} }
@@ -364,6 +366,9 @@ ble_prox_cent_connect_if_interesting(void *disc)
MODLOG_DFLT(ERROR, "Error: Failed to connect to device; addr_type=%d " MODLOG_DFLT(ERROR, "Error: Failed to connect to device; addr_type=%d "
"addr=%s; rc=%d\n", "addr=%s; rc=%d\n",
addr->type, addr_str(addr->val), rc); addr->type, addr_str(addr->val), rc);
#if !(MYNEWT_VAL(BLE_HOST_ALLOW_CONNECT_WITH_SCAN))
ble_prox_cent_scan();
#endif
return; return;
} }
} }
@@ -415,8 +420,6 @@ ble_prox_cent_gap_event(struct ble_gap_event *event, void *arg)
print_conn_desc(&desc); print_conn_desc(&desc);
MODLOG_DFLT(INFO, "\n"); MODLOG_DFLT(INFO, "\n");
link_supervision_timeout = 8 * desc.conn_itvl;
/* Remember peer. */ /* Remember peer. */
rc = peer_add(event->connect.conn_handle); rc = peer_add(event->connect.conn_handle);
if (rc != 0) { if (rc != 0) {
@@ -427,7 +430,7 @@ ble_prox_cent_gap_event(struct ble_gap_event *event, void *arg)
/* Check if this device is reconnected */ /* Check if this device is reconnected */
for (int i = 0; i <= MYNEWT_VAL(BLE_MAX_CONNECTIONS); i++) { for (int i = 0; i <= MYNEWT_VAL(BLE_MAX_CONNECTIONS); i++) {
if (disconn_peer[i].addr != NULL) { if (disconn_peer[i].addr != NULL) {
if (memcmp(disconn_peer[i].addr, &desc.peer_id_addr.val, BLE_ADDR_LEN)) { if (memcmp(disconn_peer[i].addr, &desc.peer_id_addr.val, BLE_ADDR_LEN) == 0) {
/* Peer reconnected. Stop alert for this peer */ /* Peer reconnected. Stop alert for this peer */
free(disconn_peer[i].addr); free(disconn_peer[i].addr);
disconn_peer[i].addr = NULL; disconn_peer[i].addr = NULL;
@@ -500,8 +503,10 @@ ble_prox_cent_gap_event(struct ble_gap_event *event, void *arg)
} }
} }
/* Stop calculating path loss, restart once connection is established again */ /* Stop calculating path loss, restart once connection is established again */
conn_peer[event->disconnect.conn.conn_handle].calc_path_loss = false; if (event->disconnect.conn.conn_handle <= MYNEWT_VAL(BLE_MAX_CONNECTIONS)) {
conn_peer[event->disconnect.conn.conn_handle].val_handle = 0; conn_peer[event->disconnect.conn.conn_handle].calc_path_loss = false;
conn_peer[event->disconnect.conn.conn_handle].val_handle = 0;
}
/* Forget about peer. */ /* Forget about peer. */
peer_delete(event->disconnect.conn.conn_handle); peer_delete(event->disconnect.conn.conn_handle);
@@ -726,12 +731,12 @@ app_main(void)
return; return;
} }
/* Initialize a task to keep checking path loss of the link */
ble_prox_cent_init();
for (int i = 0; i <= MYNEWT_VAL(BLE_MAX_CONNECTIONS); i++) { for (int i = 0; i <= MYNEWT_VAL(BLE_MAX_CONNECTIONS); i++) {
disconn_peer[i].addr = NULL; disconn_peer[i].addr = NULL;
disconn_peer[i].link_lost = true; disconn_peer[i].link_lost = false;
} }
/* Initialize tasks after disconn_peer array is ready */
ble_prox_cent_init();
/* Configure the host. */ /* Configure the host. */
ble_hs_cfg.reset_cb = ble_prox_cent_on_reset; ble_hs_cfg.reset_cb = ble_prox_cent_on_reset;
@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2017-2025 Espressif Systems (Shanghai) CO LTD * SPDX-FileCopyrightText: 2017-2026 Espressif Systems (Shanghai) CO LTD
* *
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
@@ -21,7 +21,7 @@
static uint8_t ext_adv_pattern_1[] = { static uint8_t ext_adv_pattern_1[] = {
0x02, BLE_HS_ADV_TYPE_FLAGS, 0x06, 0x02, BLE_HS_ADV_TYPE_FLAGS, 0x06,
0x03, BLE_HS_ADV_TYPE_COMP_UUIDS16, 0xab, 0xcd, 0x03, BLE_HS_ADV_TYPE_COMP_UUIDS16, 0xab, 0xcd,
0x03, BLE_HS_ADV_TYPE_COMP_UUIDS16, 0x18, 0x03, 0x03, BLE_HS_ADV_TYPE_COMP_UUIDS16, 0x03, 0x18, /* UUID 0x1803 in little-endian */
0x13, BLE_HS_ADV_TYPE_COMP_NAME, 'n', 'i', 'm', 'b', 'l', 'e', '-', 'p', 'r', 'o', 'x', '-', 'p', 'r', 'p', 'h', '-', 'e', 0x13, BLE_HS_ADV_TYPE_COMP_NAME, 'n', 'i', 'm', 'b', 'l', 'e', '-', 'p', 'r', 'o', 'x', '-', 'p', 'r', 'p', 'h', '-', 'e',
}; };
#endif #endif
@@ -80,8 +80,8 @@ ext_ble_prox_prph_advertise(void)
/* enable connectable advertising */ /* enable connectable advertising */
params.connectable = 1; params.connectable = 1;
/* advertise using random addr */ /* advertise using the inferred address type */
params.own_addr_type = BLE_OWN_ADDR_PUBLIC; params.own_addr_type = ble_prox_prph_addr_type;
params.primary_phy = BLE_HCI_LE_PHY_1M; params.primary_phy = BLE_HCI_LE_PHY_1M;
params.secondary_phy = BLE_HCI_LE_PHY_2M; params.secondary_phy = BLE_HCI_LE_PHY_2M;
@@ -145,9 +145,11 @@ ble_prox_prph_advertise(void)
fields.tx_pwr_lvl_is_present = 1; fields.tx_pwr_lvl_is_present = 1;
fields.tx_pwr_lvl = BLE_HS_ADV_TX_PWR_LVL_AUTO; fields.tx_pwr_lvl = BLE_HS_ADV_TX_PWR_LVL_AUTO;
#if CONFIG_BT_NIMBLE_GAP_SERVICE
fields.name = (uint8_t *)device_name; fields.name = (uint8_t *)device_name;
fields.name_len = strlen(device_name); fields.name_len = strlen(device_name);
fields.name_is_complete = 1; fields.name_is_complete = 1;
#endif
fields.uuids16 = (ble_uuid16_t[]) { fields.uuids16 = (ble_uuid16_t[]) {
BLE_UUID16_INIT(BLE_SVC_LINK_LOSS_UUID16) BLE_UUID16_INIT(BLE_SVC_LINK_LOSS_UUID16)
@@ -301,13 +303,19 @@ void app_main(void)
ble_hs_cfg.sm_sc = 1; ble_hs_cfg.sm_sc = 1;
ble_hs_cfg.sm_mitm = 1; ble_hs_cfg.sm_mitm = 1;
#if CONFIG_BT_NIMBLE_GAP_SERVICE
ble_svc_gap_init(); ble_svc_gap_init();
#endif
#if MYNEWT_VAL(BLE_GATTS)
ble_svc_gatt_init(); ble_svc_gatt_init();
#endif
#if CONFIG_BT_NIMBLE_GAP_SERVICE
int rc; int rc;
/* Set the default device name */ /* Set the default device name */
rc = ble_svc_gap_device_name_set(device_name); rc = ble_svc_gap_device_name_set(device_name);
assert(rc == 0); assert(rc == 0);
#endif
/* Start the task */ /* Start the task */
nimble_port_freertos_init(ble_prox_prph_host_task); nimble_port_freertos_init(ble_prox_prph_host_task);
@@ -11,6 +11,7 @@
#include "freertos/FreeRTOS.h" #include "freertos/FreeRTOS.h"
#include "freertos/task.h" #include "freertos/task.h"
#include "esp_err.h"
#include "esp_log.h" #include "esp_log.h"
#include "driver/spi_slave.h" #include "driver/spi_slave.h"
#include "driver/gpio.h" #include "driver/gpio.h"
@@ -100,12 +101,17 @@ void app_main(void)
t.length = 1024 * 8; t.length = 1024 * 8;
t.tx_buffer = NULL; t.tx_buffer = NULL;
t.rx_buffer = recvbuf; t.rx_buffer = recvbuf;
t.flags = SPI_SLAVE_TRANS_DMA_BUFFER_ALIGN_AUTO;
/* This call enables the SPI slave interface to receive to the recvbuf. The transaction is /* This call enables the SPI slave interface to receive to the recvbuf. The transaction is
* initialized by the SPI master, however, so it will not actually happen until the master starts a hardware transaction * initialized by the SPI master, however, so it will not actually happen until the master starts a hardware transaction
* by pulling CS low and pulsing the clock etc. * by pulling CS low and pulsing the clock etc.
*/ */
ret = spi_slave_transmit(RCV_HOST, &t, portMAX_DELAY); ret = spi_slave_transmit(RCV_HOST, &t, portMAX_DELAY);
if (ret != ESP_OK) {
ESP_LOGE("SPI_SLAVE", "spi_slave_transmit failed: %s", esp_err_to_name(ret));
continue;
}
/* Get the actual number of bytes received */ /* Get the actual number of bytes received */
int rcv_bytes = t.trans_len / 8; int rcv_bytes = t.trans_len / 8;
@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD * SPDX-FileCopyrightText: 2021-2026 Espressif Systems (Shanghai) CO LTD
* *
* SPDX-License-Identifier: Unlicense OR CC0-1.0 * SPDX-License-Identifier: Unlicense OR CC0-1.0
*/ */
@@ -69,8 +69,6 @@ static void
ble_spp_client_set_handle(const struct peer *peer) ble_spp_client_set_handle(const struct peer *peer)
{ {
const struct peer_chr *chr; const struct peer_chr *chr;
const struct peer_dsc *dsc;
uint8_t value[2];
chr = peer_chr_find_uuid(peer, chr = peer_chr_find_uuid(peer,
BLE_UUID16_DECLARE(GATT_SPP_SVC_UUID), BLE_UUID16_DECLARE(GATT_SPP_SVC_UUID),
BLE_UUID16_DECLARE(GATT_SPP_CHR_UUID)); BLE_UUID16_DECLARE(GATT_SPP_CHR_UUID));
@@ -79,6 +77,10 @@ ble_spp_client_set_handle(const struct peer *peer)
MODLOG_DFLT(ERROR, "Error: Peer lacks SPP characteristic\n"); MODLOG_DFLT(ERROR, "Error: Peer lacks SPP characteristic\n");
return; return;
} }
if (peer->conn_handle > CONFIG_BT_NIMBLE_MAX_CONNECTIONS) {
MODLOG_DFLT(ERROR, "Error: conn_handle %d out of range\n", peer->conn_handle);
return;
}
if (g_attr_handle_mutex != NULL) { if (g_attr_handle_mutex != NULL) {
xSemaphoreTake(g_attr_handle_mutex, portMAX_DELAY); xSemaphoreTake(g_attr_handle_mutex, portMAX_DELAY);
} }
@@ -88,19 +90,6 @@ ble_spp_client_set_handle(const struct peer *peer)
xSemaphoreGive(g_attr_handle_mutex); xSemaphoreGive(g_attr_handle_mutex);
} }
dsc = peer_dsc_find_uuid(peer,
BLE_UUID16_DECLARE(GATT_SPP_SVC_UUID),
BLE_UUID16_DECLARE(GATT_SPP_CHR_UUID),
BLE_UUID16_DECLARE(BLE_GATT_DSC_CLT_CFG_UUID16));
if (dsc == NULL) {
MODLOG_DFLT(ERROR, "Error: Peer lacks a CCCD for the subscribable characteristic\n");
return;
}
value[0] = 1;
value[1] = 0;
ble_gattc_write_flat(peer->conn_handle, dsc->dsc.handle,
value, sizeof(value), NULL, NULL);
} }
/** /**
@@ -247,6 +236,9 @@ ble_spp_client_connect_if_interesting(const struct ble_gap_disc_desc *disc)
rc = ble_hs_id_infer_auto(0, &own_addr_type); rc = ble_hs_id_infer_auto(0, &own_addr_type);
if (rc != 0) { if (rc != 0) {
MODLOG_DFLT(ERROR, "error determining address type; rc=%d\n", rc); MODLOG_DFLT(ERROR, "error determining address type; rc=%d\n", rc);
#if !(MYNEWT_VAL(BLE_HOST_ALLOW_CONNECT_WITH_SCAN))
ble_spp_client_scan();
#endif
return; return;
} }
@@ -260,6 +252,9 @@ ble_spp_client_connect_if_interesting(const struct ble_gap_disc_desc *disc)
MODLOG_DFLT(ERROR, "Error: Failed to connect to device; addr_type=%d " MODLOG_DFLT(ERROR, "Error: Failed to connect to device; addr_type=%d "
"addr=%s; rc=%d\n", "addr=%s; rc=%d\n",
disc->addr.type, addr_str(disc->addr.val), rc); disc->addr.type, addr_str(disc->addr.val), rc);
#if !(MYNEWT_VAL(BLE_HOST_ALLOW_CONNECT_WITH_SCAN))
ble_spp_client_scan();
#endif
return; return;
} }
} }
@@ -346,13 +341,15 @@ ble_spp_client_gap_event(struct ble_gap_event *event, void *arg)
MODLOG_DFLT(INFO, "\n"); MODLOG_DFLT(INFO, "\n");
/* Forget about peer. */ /* Forget about peer. */
memset(&connected_addr[event->disconnect.conn.conn_handle].val, 0, PEER_ADDR_VAL_SIZE); if (event->disconnect.conn.conn_handle <= CONFIG_BT_NIMBLE_MAX_CONNECTIONS) {
if (g_attr_handle_mutex != NULL) { memset(&connected_addr[event->disconnect.conn.conn_handle].val, 0, PEER_ADDR_VAL_SIZE);
xSemaphoreTake(g_attr_handle_mutex, portMAX_DELAY); if (g_attr_handle_mutex != NULL) {
} xSemaphoreTake(g_attr_handle_mutex, portMAX_DELAY);
attribute_handle[event->disconnect.conn.conn_handle] = 0; }
if (g_attr_handle_mutex != NULL) { attribute_handle[event->disconnect.conn.conn_handle] = 0;
xSemaphoreGive(g_attr_handle_mutex); if (g_attr_handle_mutex != NULL) {
xSemaphoreGive(g_attr_handle_mutex);
}
} }
peer_delete(event->disconnect.conn.conn_handle); peer_delete(event->disconnect.conn.conn_handle);
@@ -499,7 +496,11 @@ static void ble_spp_uart_init(void)
}; };
//Install UART driver, and get the queue. //Install UART driver, and get the queue.
uart_driver_install(UART_NUM_0, 4096, 8192, 10, &spp_common_uart_queue, 0); esp_err_t err = uart_driver_install(UART_NUM_0, 4096, 8192, 10, &spp_common_uart_queue, 0);
if (err != ESP_OK) {
ESP_LOGE(tag, "uart_driver_install failed: %d", err);
return;
}
//Set UART parameters //Set UART parameters
uart_param_config(UART_NUM_0, &uart_config); uart_param_config(UART_NUM_0, &uart_config);
//Set UART pins //Set UART pins
@@ -528,6 +529,7 @@ app_main(void)
g_attr_handle_mutex = xSemaphoreCreateMutex(); g_attr_handle_mutex = xSemaphoreCreateMutex();
if (g_attr_handle_mutex == NULL) { if (g_attr_handle_mutex == NULL) {
ESP_LOGE(tag, "Failed to create attribute handle mutex"); ESP_LOGE(tag, "Failed to create attribute handle mutex");
nimble_port_deinit();
return; return;
} }
@@ -411,7 +411,11 @@ static void ble_spp_uart_init(void)
.source_clk = UART_SCLK_DEFAULT, .source_clk = UART_SCLK_DEFAULT,
}; };
//Install UART driver, and get the queue. //Install UART driver, and get the queue.
uart_driver_install(UART_NUM_0, 4096, 8192, 10, &spp_common_uart_queue, 0); esp_err_t err = uart_driver_install(UART_NUM_0, 4096, 8192, 10, &spp_common_uart_queue, 0);
if (err != ESP_OK) {
ESP_LOGE("SPP_SERVER", "uart_driver_install failed: %d", err);
return;
}
//Set UART parameters //Set UART parameters
uart_param_config(UART_NUM_0, &uart_config); uart_param_config(UART_NUM_0, &uart_config);
//Set UART pins //Set UART pins
+42 -22
View File
@@ -154,6 +154,10 @@ blecent_on_custom_subscribe(uint16_t conn_handle,
MODLOG_DFLT(INFO, "\n"); MODLOG_DFLT(INFO, "\n");
peer = peer_find(conn_handle); peer = peer_find(conn_handle);
if (peer == NULL) {
MODLOG_DFLT(WARN, "Peer not found (conn_handle=%d), likely disconnected\n", conn_handle);
return 0;
}
chr = peer_chr_find_uuid(peer, chr = peer_chr_find_uuid(peer,
remote_svc_uuid, remote_svc_uuid,
remote_chr_uuid); remote_chr_uuid);
@@ -477,9 +481,6 @@ ext_blecent_should_connect(const struct ble_gap_ext_disc_desc *disc)
{ {
int offset = 0; int offset = 0;
int ad_struct_len = 0; int ad_struct_len = 0;
#if CONFIG_EXAMPLE_USE_CI_ADDRESS
uint32_t *addr_offset;
#endif // CONFIG_EXAMPLE_USE_CI_ADDRESS
uint8_t test_addr[6]; uint8_t test_addr[6];
if (disc->legacy_event_type != BLE_HCI_ADV_RPT_EVTYPE_ADV_IND && if (disc->legacy_event_type != BLE_HCI_ADV_RPT_EVTYPE_ADV_IND &&
disc->legacy_event_type != BLE_HCI_ADV_RPT_EVTYPE_DIR_IND) { disc->legacy_event_type != BLE_HCI_ADV_RPT_EVTYPE_DIR_IND) {
@@ -493,8 +494,8 @@ ext_blecent_should_connect(const struct ble_gap_ext_disc_desc *disc)
#endif #endif
#if CONFIG_EXAMPLE_USE_CI_ADDRESS #if CONFIG_EXAMPLE_USE_CI_ADDRESS
addr_offset = (uint32_t *)&test_addr[1]; uint32_t addr_val = (uint32_t)atoi(CONFIG_EXAMPLE_PEER_ADDR);
*addr_offset = atoi(CONFIG_EXAMPLE_PEER_ADDR); memcpy(&test_addr[1], &addr_val, sizeof(addr_val));
test_addr[5] = 0xC3; test_addr[5] = 0xC3;
test_addr[0] = CONFIG_IDF_FIRMWARE_CHIP_ID; test_addr[0] = CONFIG_IDF_FIRMWARE_CHIP_ID;
#endif #endif
@@ -534,9 +535,6 @@ blecent_should_connect(const struct ble_gap_disc_desc *disc)
struct ble_hs_adv_fields fields; struct ble_hs_adv_fields fields;
int rc; int rc;
int i; int i;
#if CONFIG_EXAMPLE_USE_CI_ADDRESS
uint32_t *addr_offset;
#endif // CONFIG_EXAMPLE_USE_CI_ADDRESS
uint8_t test_addr[6]; uint8_t test_addr[6];
/* The device has to be advertising connectability. */ /* The device has to be advertising connectability. */
if (disc->event_type != BLE_HCI_ADV_RPT_EVTYPE_ADV_IND && if (disc->event_type != BLE_HCI_ADV_RPT_EVTYPE_ADV_IND &&
@@ -558,8 +556,8 @@ blecent_should_connect(const struct ble_gap_disc_desc *disc)
printf("peer--> %s\n", addr_str(test_addr)); printf("peer--> %s\n", addr_str(test_addr));
#endif #endif
#if CONFIG_EXAMPLE_USE_CI_ADDRESS #if CONFIG_EXAMPLE_USE_CI_ADDRESS
addr_offset = (uint32_t *)&test_addr[1]; uint32_t addr_val = (uint32_t)atoi(CONFIG_EXAMPLE_PEER_ADDR);
*addr_offset = atoi(CONFIG_EXAMPLE_PEER_ADDR); memcpy(&test_addr[1], &addr_val, sizeof(addr_val));
test_addr[5] = 0xC3; test_addr[5] = 0xC3;
test_addr[0] = CONFIG_IDF_FIRMWARE_CHIP_ID; test_addr[0] = CONFIG_IDF_FIRMWARE_CHIP_ID;
#endif #endif
@@ -618,6 +616,9 @@ blecent_connect_if_interesting(void *disc)
rc = ble_hs_id_infer_auto(0, &own_addr_type); rc = ble_hs_id_infer_auto(0, &own_addr_type);
if (rc != 0) { if (rc != 0) {
MODLOG_DFLT(ERROR, "error determining address type; rc=%d\n", rc); MODLOG_DFLT(ERROR, "error determining address type; rc=%d\n", rc);
#if !(MYNEWT_VAL(BLE_HOST_ALLOW_CONNECT_WITH_SCAN))
blecent_scan();
#endif
return; return;
} }
@@ -636,6 +637,9 @@ blecent_connect_if_interesting(void *disc)
MODLOG_DFLT(ERROR, "Error: Failed to connect to device; addr_type=%d " MODLOG_DFLT(ERROR, "Error: Failed to connect to device; addr_type=%d "
"addr=%s; rc=%d\n", "addr=%s; rc=%d\n",
addr->type, addr_str(addr->val), rc); addr->type, addr_str(addr->val), rc);
#if !(MYNEWT_VAL(BLE_HOST_ALLOW_CONNECT_WITH_SCAN))
blecent_scan();
#endif
return; return;
} }
} }
@@ -645,17 +649,28 @@ static void blecent_power_control(uint16_t conn_handle)
{ {
int rc; int rc;
rc = ble_gap_read_remote_transmit_power_level(conn_handle, 0x01 ); // Attempting on LE 1M phy rc = ble_gap_read_remote_transmit_power_level(conn_handle, 0x01);
assert (rc == 0); if (rc != 0) {
MODLOG_DFLT(WARN, "ble_gap_read_remote_transmit_power_level failed; rc=%d\n", rc);
return;
}
rc = ble_gap_set_transmit_power_reporting_enable(conn_handle, 0x01, 0x01); rc = ble_gap_set_transmit_power_reporting_enable(conn_handle, 0x01, 0x01);
assert (rc == 0); if (rc != 0) {
MODLOG_DFLT(WARN, "ble_gap_set_transmit_power_reporting_enable failed; rc=%d\n", rc);
return;
}
rc = ble_gap_set_path_loss_reporting_param(conn_handle, 60, 10, 30, 10, 2 ); //demo values rc = ble_gap_set_path_loss_reporting_param(conn_handle, 60, 10, 30, 10, 2);
assert (rc == 0); if (rc != 0) {
MODLOG_DFLT(WARN, "ble_gap_set_path_loss_reporting_param failed; rc=%d\n", rc);
return;
}
rc = ble_gap_set_path_loss_reporting_enable(conn_handle, 0x01); rc = ble_gap_set_path_loss_reporting_enable(conn_handle, 0x01);
assert (rc == 0); if (rc != 0) {
MODLOG_DFLT(WARN, "ble_gap_set_path_loss_reporting_enable failed; rc=%d\n", rc);
}
} }
#endif #endif
@@ -936,11 +951,16 @@ blecent_gap_event(struct ble_gap_event *event, void *arg)
break; break;
} }
} }
if (i >= bearers) {
/* CID not found; nothing to remove */
return 0;
}
while (i < (bearers - 1)) { while (i < (bearers - 1)) {
cids[i] = cids[i + 1]; cids[i] = cids[i + 1];
i += 1; i += 1;
} }
cids[i] = 0; cids[i] = 0;
bearers--;
/* Now Abort */ /* Now Abort */
return 0; return 0;
@@ -1092,12 +1112,6 @@ app_main(void)
/* XXX Need to have template for store */ /* XXX Need to have template for store */
ble_store_config_init(); ble_store_config_init();
nimble_port_freertos_init(blecent_host_task);
#if CONFIG_EXAMPLE_INIT_DEINIT_LOOP
stack_init_deinit();
#endif
#if MYNEWT_VAL(BLE_EATT_CHAN_NUM) > 0 #if MYNEWT_VAL(BLE_EATT_CHAN_NUM) > 0
bearers = 0; bearers = 0;
for (int i = 0; i < MYNEWT_VAL(BLE_EATT_CHAN_NUM); i++) { for (int i = 0; i < MYNEWT_VAL(BLE_EATT_CHAN_NUM); i++) {
@@ -1105,4 +1119,10 @@ app_main(void)
} }
#endif #endif
nimble_port_freertos_init(blecent_host_task);
#if CONFIG_EXAMPLE_INIT_DEINIT_LOOP
stack_init_deinit();
#endif
} }
@@ -245,7 +245,20 @@ gatt_svr_chr_access_sc_control_point(uint16_t conn_handle,
break; break;
} }
/* Append response value */ /* Append Response Op Code, Request Op Code, and Response Value */
uint8_t rsp_op = SC_CP_OP_RESPONSE;
rc = os_mbuf_append(om_indication, &rsp_op, sizeof(rsp_op));
if (rc != 0) {
os_mbuf_free_chain(om_indication);
return BLE_ATT_ERR_INSUFFICIENT_RES;
}
rc = os_mbuf_append(om_indication, &op_code, sizeof(op_code));
if (rc != 0) {
os_mbuf_free_chain(om_indication);
return BLE_ATT_ERR_INSUFFICIENT_RES;
}
rc = os_mbuf_append(om_indication, &response, sizeof(response)); rc = os_mbuf_append(om_indication, &response, sizeof(response));
if (rc != 0){ if (rc != 0){
@@ -267,14 +280,7 @@ gatt_svr_chr_access_sc_control_point(uint16_t conn_handle,
rc = ble_gatts_indicate_custom(conn_handle, csc_control_point_handle, rc = ble_gatts_indicate_custom(conn_handle, csc_control_point_handle,
om_indication); om_indication);
if (rc != 0) { /* om_indication is consumed (freed) by ble_gatts_indicate_custom regardless of result */
goto done;
}
return rc;
done:
os_mbuf_free_chain(om_indication);
return rc; return rc;
} }
@@ -328,6 +334,9 @@ gatt_svr_chr_notify_csc_measurement(uint16_t conn_handle)
#endif #endif
om = ble_hs_mbuf_from_flat(data_buf, data_offset); om = ble_hs_mbuf_from_flat(data_buf, data_offset);
if (om == NULL) {
return BLE_HS_ENOMEM;
}
rc = ble_gatts_notify_custom(conn_handle, csc_measurement_handle, om); rc = ble_gatts_notify_custom(conn_handle, csc_measurement_handle, om);
return rc; return rc;
+11 -3
View File
@@ -24,6 +24,7 @@
#include "console/console.h" #include "console/console.h"
#include "nimble/ble.h" #include "nimble/ble.h"
#include "host/ble_hs.h" #include "host/ble_hs.h"
#include "host/util/util.h"
#include "services/gap/ble_svc_gap.h" #include "services/gap/ble_svc_gap.h"
#include "blecsc_sens.h" #include "blecsc_sens.h"
#include "nimble/nimble_port.h" #include "nimble/nimble_port.h"
@@ -198,14 +199,16 @@ blecsc_measurement(struct ble_npl_event *ev)
{ {
int rc; int rc;
rc = ble_npl_callout_reset(&blecsc_measure_timer, portTICK_PERIOD_MS * 10); rc = ble_npl_callout_reset(&blecsc_measure_timer, ble_npl_time_ms_to_ticks32(1000));
assert(rc == 0); assert(rc == 0);
blecsc_simulate_speed_and_cadence(); blecsc_simulate_speed_and_cadence();
if (notify_state) { if (notify_state) {
rc = gatt_svr_chr_notify_csc_measurement(conn_handle); rc = gatt_svr_chr_notify_csc_measurement(conn_handle);
assert(rc == 0); if (rc != 0) {
MODLOG_DFLT(WARN, "gatt_svr_chr_notify_csc_measurement failed; rc=%d\n", rc);
}
} }
} }
@@ -232,6 +235,8 @@ blecsc_gap_event(struct ble_gap_event *event, void *arg)
case BLE_GAP_EVENT_DISCONNECT: case BLE_GAP_EVENT_DISCONNECT:
MODLOG_DFLT(INFO, "disconnect; reason=%d\n", event->disconnect.reason); MODLOG_DFLT(INFO, "disconnect; reason=%d\n", event->disconnect.reason);
conn_handle = 0; conn_handle = 0;
notify_state = false;
gatt_svr_set_cp_indicate(0);
/* Connection terminated; resume advertising */ /* Connection terminated; resume advertising */
blecsc_advertise(); blecsc_advertise();
break; break;
@@ -272,6 +277,9 @@ blecsc_on_sync(void)
{ {
int rc; int rc;
rc = ble_hs_util_ensure_addr(0);
assert(rc == 0);
/* Figure out address to use while advertising (no privacy) */ /* Figure out address to use while advertising (no privacy) */
rc = ble_hs_id_infer_auto(0, &blecsc_addr_type); rc = ble_hs_id_infer_auto(0, &blecsc_addr_type);
assert(rc == 0); assert(rc == 0);
@@ -321,7 +329,7 @@ app_main(void)
/* Initialize measurement and notification timer */ /* Initialize measurement and notification timer */
ble_npl_callout_init(&blecsc_measure_timer, nimble_port_get_dflt_eventq(), ble_npl_callout_init(&blecsc_measure_timer, nimble_port_get_dflt_eventq(),
blecsc_measurement, NULL); blecsc_measurement, NULL);
rc = ble_npl_callout_reset(&blecsc_measure_timer, portTICK_PERIOD_MS * 100); rc = ble_npl_callout_reset(&blecsc_measure_timer, ble_npl_time_ms_to_ticks32(1000));
assert(rc == 0); assert(rc == 0);
#if MYNEWT_VAL(BLE_GATTS) #if MYNEWT_VAL(BLE_GATTS)
+14 -25
View File
@@ -130,23 +130,7 @@ blehr_advertise(void)
static void static void
blehr_tx_hrate_stop(void) blehr_tx_hrate_stop(void)
{ {
xTimerStop( blehr_tx_timer, 1000 / portTICK_PERIOD_MS ); xTimerStop(blehr_tx_timer, 0);
}
/* Reset heart rate measurement */
static void
blehr_tx_hrate_reset(void)
{
int rc;
if (xTimerReset(blehr_tx_timer, 1000 / portTICK_PERIOD_MS ) == pdPASS) {
rc = 0;
} else {
rc = 1;
}
assert(rc == 0);
} }
/* This function simulates heart beat and notifies it to the client */ /* This function simulates heart beat and notifies it to the client */
@@ -173,11 +157,15 @@ blehr_tx_hrate(TimerHandle_t ev)
} }
om = ble_hs_mbuf_from_flat(hrm, sizeof(hrm)); om = ble_hs_mbuf_from_flat(hrm, sizeof(hrm));
if (om == NULL) {
MODLOG_DFLT(WARN, "ble_hs_mbuf_from_flat failed; out of memory\n");
return;
}
rc = ble_gatts_notify_custom(conn_handle, hrs_hrm_handle, om); rc = ble_gatts_notify_custom(conn_handle, hrs_hrm_handle, om);
if (rc != 0) {
assert(rc == 0); MODLOG_DFLT(WARN, "ble_gatts_notify_custom failed; rc=%d\n", rc);
}
blehr_tx_hrate_reset();
} }
static int static int
@@ -218,10 +206,11 @@ blehr_gap_event(struct ble_gap_event *event, void *arg)
event->subscribe.cur_notify, hrs_hrm_handle); event->subscribe.cur_notify, hrs_hrm_handle);
if (event->subscribe.attr_handle == hrs_hrm_handle) { if (event->subscribe.attr_handle == hrs_hrm_handle) {
notify_state = event->subscribe.cur_notify; notify_state = event->subscribe.cur_notify;
blehr_tx_hrate_reset(); if (notify_state) {
} else if (event->subscribe.attr_handle != hrs_hrm_handle) { xTimerStart(blehr_tx_timer, 0);
notify_state = event->subscribe.cur_notify; } else {
blehr_tx_hrate_stop(); blehr_tx_hrate_stop();
}
} }
ESP_LOGI("BLE_GAP_SUBSCRIBE_EVENT", "conn_handle from subscribe=%d", conn_handle); ESP_LOGI("BLE_GAP_SUBSCRIBE_EVENT", "conn_handle from subscribe=%d", conn_handle);
break; break;
@@ -164,9 +164,11 @@ gatt_svc_access(uint16_t conn_handle, uint16_t attr_handle,
sizeof(gatt_svr_chr_val), sizeof(gatt_svr_chr_val),
sizeof(gatt_svr_chr_val), sizeof(gatt_svr_chr_val),
&gatt_svr_chr_val, NULL); &gatt_svr_chr_val, NULL);
ble_gatts_chr_updated(attr_handle); if (rc == 0) {
MODLOG_DFLT(INFO, "Notification/Indication scheduled for " ble_gatts_chr_updated(attr_handle);
"all subscribed peers.\n"); MODLOG_DFLT(INFO, "Notification/Indication scheduled for "
"all subscribed peers.\n");
}
return rc; return rc;
} }
goto unknown; goto unknown;
@@ -183,7 +185,7 @@ gatt_svc_access(uint16_t conn_handle, uint16_t attr_handle,
if (ble_uuid_cmp(uuid, &gatt_svr_dsc_uuid.u) == 0) { if (ble_uuid_cmp(uuid, &gatt_svr_dsc_uuid.u) == 0) {
rc = os_mbuf_append(ctxt->om, rc = os_mbuf_append(ctxt->om,
&gatt_svr_dsc_val, &gatt_svr_dsc_val,
sizeof(gatt_svr_chr_val)); sizeof(gatt_svr_dsc_val));
return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES; return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
} }
goto unknown; goto unknown;
+37 -18
View File
@@ -106,8 +106,8 @@ ext_bleprph_advertise(void)
/* enable connectable advertising */ /* enable connectable advertising */
params.connectable = 1; params.connectable = 1;
/* advertise using random addr */ /* advertise using configured/inferred addr type */
params.own_addr_type = BLE_OWN_ADDR_PUBLIC; params.own_addr_type = own_addr_type;
params.primary_phy = BLE_HCI_LE_PHY_1M; params.primary_phy = BLE_HCI_LE_PHY_1M;
params.secondary_phy = BLE_HCI_LE_PHY_2M; params.secondary_phy = BLE_HCI_LE_PHY_2M;
@@ -186,9 +186,10 @@ bleprph_advertise(void)
fields.name_is_complete = 1; fields.name_is_complete = 1;
#endif #endif
fields.uuids16 = (ble_uuid16_t[]) { static const ble_uuid16_t adv_uuids16[] = {
BLE_UUID16_INIT(GATT_SVR_SVC_ALERT_UUID) BLE_UUID16_INIT(GATT_SVR_SVC_ALERT_UUID)
}; };
fields.uuids16 = adv_uuids16;
fields.num_uuids16 = 1; fields.num_uuids16 = 1;
fields.uuids16_is_complete = 1; fields.uuids16_is_complete = 1;
@@ -216,11 +217,16 @@ static void bleprph_power_control(uint16_t conn_handle)
{ {
int rc; int rc;
rc = ble_gap_read_remote_transmit_power_level(conn_handle, 0x01 ); // Attempting on LE 1M phy rc = ble_gap_read_remote_transmit_power_level(conn_handle, 0x01);
assert (rc == 0); if (rc != 0) {
MODLOG_DFLT(WARN, "ble_gap_read_remote_transmit_power_level failed; rc=%d\n", rc);
return;
}
rc = ble_gap_set_transmit_power_reporting_enable(conn_handle, 0x1, 0x1); rc = ble_gap_set_transmit_power_reporting_enable(conn_handle, 0x1, 0x1);
assert (rc == 0); if (rc != 0) {
MODLOG_DFLT(WARN, "ble_gap_set_transmit_power_reporting_enable failed; rc=%d\n", rc);
}
} }
#endif #endif
@@ -272,7 +278,9 @@ bleprph_gap_event(struct ble_gap_event *event, void *arg)
} }
#if MYNEWT_VAL(BLE_POWER_CONTROL) #if MYNEWT_VAL(BLE_POWER_CONTROL)
bleprph_power_control(event->connect.conn_handle); if (event->connect.status == 0) {
bleprph_power_control(event->connect.conn_handle);
}
#endif #endif
return 0; return 0;
@@ -281,6 +289,13 @@ bleprph_gap_event(struct ble_gap_event *event, void *arg)
bleprph_print_conn_desc(&event->disconnect.conn); bleprph_print_conn_desc(&event->disconnect.conn);
MODLOG_DFLT(INFO, "\n"); MODLOG_DFLT(INFO, "\n");
#if MYNEWT_VAL(BLE_EATT_CHAN_NUM) > 0
bearers = 0;
for (int i = 0; i < MYNEWT_VAL(BLE_EATT_CHAN_NUM); i++) {
cids[i] = 0;
}
#endif
/* Connection terminated; resume advertising. */ /* Connection terminated; resume advertising. */
#if CONFIG_EXAMPLE_EXTENDED_ADV #if CONFIG_EXAMPLE_EXTENDED_ADV
ext_bleprph_advertise(); ext_bleprph_advertise();
@@ -491,17 +506,21 @@ bleprph_on_reset(int reason)
static void static void
ble_app_set_addr(void) ble_app_set_addr(void)
{ {
ble_addr_t addr; ble_addr_t addr = {0};
int rc; int rc;
/* generate new non-resolvable private address */ /* generate new non-resolvable private address */
rc = ble_hs_id_gen_rnd(0, &addr); rc = ble_hs_id_gen_rnd(0, &addr);
assert(rc == 0); if (rc != 0) {
MODLOG_DFLT(ERROR, "ble_hs_id_gen_rnd failed; rc=%d\n", rc);
return;
}
/* set generated address */ /* set generated address */
rc = ble_hs_id_set_rnd(addr.val); rc = ble_hs_id_set_rnd(addr.val);
if (rc != 0) {
assert(rc == 0); MODLOG_DFLT(ERROR, "ble_hs_id_set_rnd failed; rc=%d\n", rc);
}
} }
#endif #endif
@@ -621,6 +640,13 @@ app_main(void)
/* XXX Need to have template for store */ /* XXX Need to have template for store */
ble_store_config_init(); ble_store_config_init();
#if MYNEWT_VAL(BLE_EATT_CHAN_NUM) > 0
bearers = 0;
for (int i = 0; i < MYNEWT_VAL(BLE_EATT_CHAN_NUM); i++) {
cids[i] = 0;
}
#endif
nimble_port_freertos_init(bleprph_host_task); nimble_port_freertos_init(bleprph_host_task);
/* Initialize command line interface to accept input from user */ /* Initialize command line interface to accept input from user */
@@ -628,11 +654,4 @@ app_main(void)
if (rc != ESP_OK) { if (rc != ESP_OK) {
ESP_LOGE(tag, "scli_init() failed"); ESP_LOGE(tag, "scli_init() failed");
} }
#if MYNEWT_VAL(BLE_EATT_CHAN_NUM) > 0
bearers = 0;
for (int i = 0; i < MYNEWT_VAL(BLE_EATT_CHAN_NUM); i++) {
cids[i] = 0;
}
#endif
} }
@@ -145,9 +145,11 @@ gatt_svc_access(uint16_t conn_handle, uint16_t attr_handle,
sizeof(gatt_svr_chr_val), sizeof(gatt_svr_chr_val),
sizeof(gatt_svr_chr_val), sizeof(gatt_svr_chr_val),
&gatt_svr_chr_val, NULL); &gatt_svr_chr_val, NULL);
ble_gatts_chr_updated(attr_handle); if (rc == 0) {
MODLOG_DFLT(INFO, "Notification/Indication scheduled for " ble_gatts_chr_updated(attr_handle);
"all subscribed peers.\n"); MODLOG_DFLT(INFO, "Notification/Indication scheduled for "
"all subscribed peers.\n");
}
return rc; return rc;
} }
goto unknown; goto unknown;
@@ -164,7 +166,7 @@ gatt_svc_access(uint16_t conn_handle, uint16_t attr_handle,
if (ble_uuid_cmp(uuid, &gatt_svr_dsc_uuid.u) == 0) { if (ble_uuid_cmp(uuid, &gatt_svr_dsc_uuid.u) == 0) {
rc = os_mbuf_append(ctxt->om, rc = os_mbuf_append(ctxt->om,
&gatt_svr_dsc_val, &gatt_svr_dsc_val,
sizeof(gatt_svr_chr_val)); sizeof(gatt_svr_dsc_val));
return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES; return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
} }
goto unknown; goto unknown;
@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2015-2025 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
*/ */
@@ -85,8 +85,8 @@ ext_bleprph_advertise(void)
/* enable connectable advertising */ /* enable connectable advertising */
params.connectable = 1; params.connectable = 1;
/* advertise using random addr */ /* advertise using configured/inferred addr type */
params.own_addr_type = BLE_OWN_ADDR_PUBLIC; params.own_addr_type = own_addr_type;
params.primary_phy = BLE_HCI_LE_PHY_1M; params.primary_phy = BLE_HCI_LE_PHY_1M;
params.secondary_phy = BLE_HCI_LE_PHY_2M; params.secondary_phy = BLE_HCI_LE_PHY_2M;
@@ -155,15 +155,18 @@ bleprph_advertise(void)
fields.tx_pwr_lvl_is_present = 1; fields.tx_pwr_lvl_is_present = 1;
fields.tx_pwr_lvl = BLE_HS_ADV_TX_PWR_LVL_AUTO; fields.tx_pwr_lvl = BLE_HS_ADV_TX_PWR_LVL_AUTO;
#if CONFIG_BT_NIMBLE_GAP_SERVICE
const char *name; const char *name;
name = ble_svc_gap_device_name(); name = ble_svc_gap_device_name();
fields.name = (uint8_t *)name; fields.name = (uint8_t *)name;
fields.name_len = strlen(name); fields.name_len = strlen(name);
fields.name_is_complete = 1; fields.name_is_complete = 1;
#endif
fields.uuids16 = (ble_uuid16_t[]) { static const ble_uuid16_t adv_uuids16[] = {
BLE_UUID16_INIT(GATT_SVR_SVC_ALERT_UUID) BLE_UUID16_INIT(GATT_SVR_SVC_ALERT_UUID)
}; };
fields.uuids16 = adv_uuids16;
fields.num_uuids16 = 1; fields.num_uuids16 = 1;
fields.uuids16_is_complete = 1; fields.uuids16_is_complete = 1;
@@ -191,11 +194,16 @@ static void bleprph_power_control(uint16_t conn_handle)
{ {
int rc; int rc;
rc = ble_gap_read_remote_transmit_power_level(conn_handle, 0x01 ); // Attempting on LE 1M phy rc = ble_gap_read_remote_transmit_power_level(conn_handle, 0x01);
assert (rc == 0); if (rc != 0) {
MODLOG_DFLT(WARN, "ble_gap_read_remote_transmit_power_level failed; rc=%d\n", rc);
return;
}
rc = ble_gap_set_transmit_power_reporting_enable(conn_handle, 0x1, 0x1); rc = ble_gap_set_transmit_power_reporting_enable(conn_handle, 0x1, 0x1);
assert (rc == 0); if (rc != 0) {
MODLOG_DFLT(WARN, "ble_gap_set_transmit_power_reporting_enable failed; rc=%d\n", rc);
}
} }
#endif #endif
@@ -419,17 +427,21 @@ bleprph_on_reset(int reason)
static void static void
ble_app_set_addr(void) ble_app_set_addr(void)
{ {
ble_addr_t addr; ble_addr_t addr = {0};
int rc; int rc;
/* generate new non-resolvable private address */ /* generate new non-resolvable private address */
rc = ble_hs_id_gen_rnd(0, &addr); rc = ble_hs_id_gen_rnd(0, &addr);
assert(rc == 0); if (rc != 0) {
MODLOG_DFLT(ERROR, "ble_hs_id_gen_rnd failed; rc=%d\n", rc);
return;
}
/* set generated address */ /* set generated address */
rc = ble_hs_id_set_rnd(addr.val); rc = ble_hs_id_set_rnd(addr.val);
if (rc != 0) {
assert(rc == 0); MODLOG_DFLT(ERROR, "ble_hs_id_set_rnd failed; rc=%d\n", rc);
}
} }
#endif #endif
@@ -53,18 +53,36 @@ static void IRAM_ATTR hci_uart_rx_task(void *arg)
int len_now_read = -1; int len_now_read = -1;
uint32_t len_to_read = 1; uint32_t len_to_read = 1;
uint32_t len_total_read = 0; uint32_t len_total_read = 0;
uint32_t len_state_read = 0;
uint8_t rx_st = UART_RX_TYPE; uint8_t rx_st = UART_RX_TYPE;
while (!s_shutdown_flag) { while (!s_shutdown_flag) {
// Use timeout instead of portMAX_DELAY to allow periodic shutdown flag check if (len_state_read < len_to_read) {
len_now_read = uart_read_bytes(UART_NO, &buf[len_total_read], len_to_read, pdMS_TO_TICKS(100)); // Use timeout instead of portMAX_DELAY to allow periodic shutdown flag check
len_now_read = uart_read_bytes(UART_NO, &buf[len_total_read],
len_to_read - len_state_read,
pdMS_TO_TICKS(100));
// If timeout occurred, continue loop to check shutdown flag again // If timeout occurred, continue loop to check shutdown flag again
if (len_now_read == 0) { if (len_now_read == 0) {
continue; continue;
}
if (len_now_read < 0) {
ESP_LOGE(TAG, "uart_read_bytes failed: %d", len_now_read);
rx_st = UART_RX_TYPE;
len_to_read = 1;
len_total_read = 0;
len_state_read = 0;
continue;
}
len_total_read += len_now_read;
len_state_read += len_now_read;
if (len_state_read < len_to_read) {
continue;
}
} }
len_state_read = 0;
len_total_read += len_now_read;
switch (rx_st) { switch (rx_st) {
case UART_RX_TYPE: { case UART_RX_TYPE: {
@@ -83,6 +101,18 @@ static void IRAM_ATTR hci_uart_rx_task(void *arg)
case UART_RX_LEN: { case UART_RX_LEN: {
if (buf[0] == DATA_TYPE_ACL) { if (buf[0] == DATA_TYPE_ACL) {
len_to_read = buf[3] | (buf[4] << 8); len_to_read = buf[3] | (buf[4] << 8);
if (len_total_read + len_to_read > sizeof(buf)) {
ESP_LOGE(TAG, "ACL packet length %lu exceeds buffer size, discarding", (unsigned long)len_to_read);
/* Drain the unread payload bytes so the UART stream stays
* synchronised; without this the next UART_RX_TYPE read
* would pick up a payload byte, not a valid H4 type byte. */
uart_flush_input(UART_NO);
rx_st = UART_RX_TYPE;
len_to_read = 1;
len_total_read = 0;
len_state_read = 0;
break;
}
} else if (buf[0] == DATA_TYPE_EVENT) { } else if (buf[0] == DATA_TYPE_EVENT) {
len_to_read = buf[2]; len_to_read = buf[2];
} else { } else {
@@ -107,6 +137,10 @@ static void IRAM_ATTR hci_uart_rx_task(void *arg)
ESP_LOGE(TAG, "Received HCI data length at host (%d)" ESP_LOGE(TAG, "Received HCI data length at host (%d)"
"exceeds maximum configured HCI event buffer size (%d).", "exceeds maximum configured HCI event buffer size (%d).",
totlen, MYNEWT_VAL(BLE_TRANSPORT_EVT_SIZE)); totlen, MYNEWT_VAL(BLE_TRANSPORT_EVT_SIZE));
rx_st = UART_RX_TYPE;
len_to_read = 1;
len_total_read = 0;
len_state_read = 0;
break; break;
} }
@@ -121,6 +155,10 @@ static void IRAM_ATTR hci_uart_rx_task(void *arg)
/* Skip advertising report if we're out of memory */ /* Skip advertising report if we're out of memory */
if (!evbuf) { if (!evbuf) {
ESP_LOGE(TAG, "No buffers"); ESP_LOGE(TAG, "No buffers");
rx_st = UART_RX_TYPE;
len_to_read = 1;
len_total_read = 0;
len_state_read = 0;
break; break;
} }
} else { } else {
@@ -141,13 +179,18 @@ static void IRAM_ATTR hci_uart_rx_task(void *arg)
rx_st = UART_RX_TYPE; rx_st = UART_RX_TYPE;
len_to_read = 1; len_to_read = 1;
len_total_read = 0; len_total_read = 0;
len_state_read = 0;
break; break;
} }
if ((rc = os_mbuf_append(m, &data[1], len_total_read - 1)) != 0) { if ((rc = os_mbuf_append(m, &data[1], len_total_read - 1)) != 0) {
ESP_LOGE(TAG, "%s failed to os_mbuf_append; rc = %d", __func__, rc); ESP_LOGE(TAG, "%s failed to os_mbuf_append; rc = %d", __func__, rc);
os_mbuf_free_chain(m); os_mbuf_free_chain(m);
return; rx_st = UART_RX_TYPE;
len_to_read = 1;
len_total_read = 0;
len_state_read = 0;
break;
} }
ble_transport_to_hs_acl(m); ble_transport_to_hs_acl(m);
@@ -156,6 +199,7 @@ static void IRAM_ATTR hci_uart_rx_task(void *arg)
rx_st = UART_RX_TYPE; rx_st = UART_RX_TYPE;
len_to_read = 1; len_to_read = 1;
len_total_read = 0; len_total_read = 0;
len_state_read = 0;
} }
break; break;
@@ -166,7 +210,8 @@ static void IRAM_ATTR hci_uart_rx_task(void *arg)
} }
} }
vTaskDelete(NULL); s_rx_task_hdl = NULL;
vTaskDelete(NULL);
} }
void hci_uart_send(uint8_t *buf, uint16_t len) void hci_uart_send(uint8_t *buf, uint16_t len)
@@ -176,7 +221,10 @@ void hci_uart_send(uint8_t *buf, uint16_t len)
while (len) { while (len) {
len_write = uart_write_bytes(UART_NO, p, len); len_write = uart_write_bytes(UART_NO, p, len);
assert(len_write > 0); if (len_write <= 0) {
ESP_LOGE(TAG, "uart_write_bytes failed: %d", len_write);
return;
}
len -= len_write; len -= len_write;
p += len_write; p += len_write;
} }
@@ -216,7 +264,7 @@ int
ble_transport_to_ll_cmd_impl(void *buf) ble_transport_to_ll_cmd_impl(void *buf)
{ {
int len = 3 + ((uint8_t *)buf)[2] + 1; int len = 3 + ((uint8_t *)buf)[2] + 1;
uint8_t data[258]; uint8_t data[259];
data[0] = HCI_H4_CMD; data[0] = HCI_H4_CMD;
memcpy(data + 1, buf, len - 1); memcpy(data + 1, buf, len - 1);
hci_uart_send(data, len); hci_uart_send(data, len);
@@ -262,7 +310,7 @@ void hci_uart_close(void)
const int max_wait_count = 5; const int max_wait_count = 5;
TaskHandle_t task_handle = s_rx_task_hdl; TaskHandle_t task_handle = s_rx_task_hdl;
while (wait_count < max_wait_count) { while (wait_count < max_wait_count && s_rx_task_hdl == task_handle) {
vTaskDelay(pdMS_TO_TICKS(100)); vTaskDelay(pdMS_TO_TICKS(100));
wait_count++; wait_count++;
@@ -272,8 +320,8 @@ void hci_uart_close(void)
if (s_rx_task_hdl == task_handle) { if (s_rx_task_hdl == task_handle) {
vTaskDelete(s_rx_task_hdl); vTaskDelete(s_rx_task_hdl);
vTaskDelay(pdMS_TO_TICKS(100)); vTaskDelay(pdMS_TO_TICKS(100));
s_rx_task_hdl = NULL;
} }
s_rx_task_hdl = NULL;
} }
uart_driver_delete(UART_NO); uart_driver_delete(UART_NO);
@@ -92,6 +92,10 @@ static void event_handler(void* arg, esp_event_base_t event_base,
void wifi_init_sta(void) void wifi_init_sta(void)
{ {
s_wifi_event_group = xEventGroupCreate(); s_wifi_event_group = xEventGroupCreate();
if (s_wifi_event_group == NULL) {
ESP_LOGE(TAG, "Failed to create wifi event group");
return;
}
ESP_ERROR_CHECK(esp_netif_init()); ESP_ERROR_CHECK(esp_netif_init());
@@ -195,7 +199,7 @@ static void cmd_ping_on_ping_end(esp_ping_handle_t hdl, void *args)
esp_ping_get_profile(hdl, ESP_PING_PROF_REPLY, &received, sizeof(received)); esp_ping_get_profile(hdl, ESP_PING_PROF_REPLY, &received, sizeof(received));
esp_ping_get_profile(hdl, ESP_PING_PROF_IPADDR, &target_addr, sizeof(target_addr)); esp_ping_get_profile(hdl, ESP_PING_PROF_IPADDR, &target_addr, sizeof(target_addr));
esp_ping_get_profile(hdl, ESP_PING_PROF_DURATION, &total_time_ms, sizeof(total_time_ms)); esp_ping_get_profile(hdl, ESP_PING_PROF_DURATION, &total_time_ms, sizeof(total_time_ms));
uint32_t loss = (uint32_t)((1 - ((float)received) / transmitted) * 100); uint32_t loss = (transmitted > 0) ? (uint32_t)((1 - ((float)received) / transmitted) * 100) : 0;
if (IP_IS_V4(&target_addr)) { if (IP_IS_V4(&target_addr)) {
printf("\n--- %s ping statistics ---\n", inet_ntoa(*ip_2_ip4(&target_addr))); printf("\n--- %s ping statistics ---\n", inet_ntoa(*ip_2_ip4(&target_addr)));
} else { } else {
@@ -347,9 +351,10 @@ bleprph_advertise(void)
fields.name_len = strlen(name); fields.name_len = strlen(name);
fields.name_is_complete = 1; fields.name_is_complete = 1;
fields.uuids16 = (ble_uuid16_t[]) { static const ble_uuid16_t adv_uuids16[] = {
BLE_UUID16_INIT(GATT_SVR_SVC_ALERT_UUID) BLE_UUID16_INIT(GATT_SVR_SVC_ALERT_UUID)
}; };
fields.uuids16 = adv_uuids16;
fields.num_uuids16 = 1; fields.num_uuids16 = 1;
fields.uuids16_is_complete = 1; fields.uuids16_is_complete = 1;
@@ -12,12 +12,20 @@
int int
peer_addr_parse(const char *addr_str, uint8_t addr[PEER_ADDR_VAL_SIZE]) peer_addr_parse(const char *addr_str, uint8_t addr[PEER_ADDR_VAL_SIZE])
{ {
unsigned int tmp[6] = {0};
int rc;
int i;
if (addr_str == NULL) { if (addr_str == NULL) {
return 0; return 0;
} }
return sscanf(addr_str, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx", rc = sscanf(addr_str, "%x:%x:%x:%x:%x:%x",
&addr[5], &addr[4], &addr[3], &tmp[5], &tmp[4], &tmp[3],
&addr[2], &addr[1], &addr[0]); &tmp[2], &tmp[1], &tmp[0]);
for (i = 0; i < PEER_ADDR_VAL_SIZE; i++) {
addr[i] = (uint8_t)tmp[i];
}
return rc;
} }
/** /**
@@ -98,7 +106,7 @@ print_conn_desc(const struct ble_gap_conn_desc *desc)
} }
#if MYNEWT_VAL(BLE_EXT_ADV) #if MYNEWT_VAL(BLE_EXT_ADV)
void static void
print_addr(const void *addr, const char *name) print_addr(const void *addr, const char *name)
{ {
const uint8_t *u8p; const uint8_t *u8p;
@@ -254,11 +262,9 @@ print_adv_fields(const struct ble_hs_adv_fields *fields)
if (fields->device_addr_is_present) { if (fields->device_addr_is_present) {
MODLOG_DFLT(DEBUG, " device_addr="); MODLOG_DFLT(DEBUG, " device_addr=");
u8p = fields->device_addr; u8p = fields->device_addr;
MODLOG_DFLT(DEBUG, "%s ", addr_str(u8p)); MODLOG_DFLT(DEBUG, "%s addr_type %d ", addr_str(u8p),
fields->device_addr_type);
u8p += BLE_HS_ADV_PUBLIC_TGT_ADDR_ENTRY_LEN;
MODLOG_DFLT(DEBUG, "addr_type %d ", *u8p);
} }
if (fields->le_role_is_present) { if (fields->le_role_is_present) {
@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2021-2023 Espressif Systems (Shanghai) CO LTD * SPDX-FileCopyrightText: 2021-2026 Espressif Systems (Shanghai) CO LTD
* *
* SPDX-License-Identifier: Unlicense OR CC0-1.0 * SPDX-License-Identifier: Unlicense OR CC0-1.0
*/ */
@@ -167,7 +167,7 @@ peer_dsc_add(struct peer *peer, uint16_t chr_val_handle,
if (prev == NULL) { if (prev == NULL) {
SLIST_INSERT_HEAD(&chr->dscs, dsc, next); SLIST_INSERT_HEAD(&chr->dscs, dsc, next);
} else { } else {
SLIST_NEXT(prev, next) = dsc; SLIST_INSERT_AFTER(prev, dsc, next);
} }
return 0; return 0;
@@ -196,6 +196,7 @@ peer_disc_dscs(struct peer *peer)
peer_dsc_disced, peer); peer_dsc_disced, peer);
if (rc != 0) { if (rc != 0) {
peer_disc_complete(peer, rc); peer_disc_complete(peer, rc);
return;
} }
peer->disc_prev_chr_val = chr->chr.val_handle; peer->disc_prev_chr_val = chr->chr.val_handle;
@@ -357,7 +358,7 @@ peer_chr_add(struct peer *peer, uint16_t svc_start_handle,
if (prev == NULL) { if (prev == NULL) {
SLIST_INSERT_HEAD(&svc->chrs, chr, next); SLIST_INSERT_HEAD(&svc->chrs, chr, next);
} else { } else {
SLIST_NEXT(prev, next) = chr; SLIST_INSERT_AFTER(prev, chr, next);
} }
return 0; return 0;
@@ -407,12 +408,15 @@ peer_disc_chrs(struct peer *peer)
struct peer_svc *svc; struct peer_svc *svc;
int rc; int rc;
/* Search through the list of discovered service for the first service that /* Advance past the last processed service, or start from the beginning. */
* contains undiscovered characteristics. Then, discover all if (peer->cur_svc == NULL) {
* characteristics belonging to that service. svc = SLIST_FIRST(&peer->svcs);
*/ } else {
SLIST_FOREACH(svc, &peer->svcs, next) { svc = SLIST_NEXT(peer->cur_svc, next);
if (!peer_svc_is_empty(svc) && SLIST_EMPTY(&svc->chrs)) { }
for (; svc != NULL; svc = SLIST_NEXT(svc, next)) {
if (!peer_svc_is_empty(svc)) {
peer->cur_svc = svc; peer->cur_svc = svc;
rc = ble_gattc_disc_all_chrs(peer->conn_handle, rc = ble_gattc_disc_all_chrs(peer->conn_handle,
svc->svc.start_handle, svc->svc.start_handle,
@@ -596,17 +600,24 @@ peer_disc_incs(struct peer *peer)
if (peer->cur_svc == NULL) { if (peer->cur_svc == NULL) {
if (peer->disc_prev_chr_val > 0) { if (peer->disc_prev_chr_val > 0) {
peer_disc_chrs(peer); peer_disc_chrs(peer);
return;
} }
return;
} }
} }
/* Skip empty services rather than aborting include discovery early. */
while (peer->cur_svc != NULL && peer_svc_is_empty(peer->cur_svc)) {
peer->cur_svc = SLIST_NEXT(peer->cur_svc, next);
}
svc = peer->cur_svc; svc = peer->cur_svc;
if (svc != NULL && !peer_svc_is_empty(svc)) { if (svc != NULL) {
rc = ble_gattc_find_inc_svcs(peer->conn_handle, rc = ble_gattc_find_inc_svcs(peer->conn_handle,
svc->svc.start_handle, svc->svc.start_handle,
svc->svc.end_handle, svc->svc.end_handle,
peer_inc_disced, peer); peer_inc_disced, peer);
if (rc != 0) { if (rc != 0) {
peer->cur_svc = NULL;
peer_disc_chrs(peer); peer_disc_chrs(peer);
} }
} else { } else {
@@ -684,6 +695,10 @@ peer_svc_find_uuid(const struct peer *peer, const ble_uuid_t *uuid)
{ {
const struct peer_svc *svc; const struct peer_svc *svc;
if (peer == NULL || uuid == NULL) {
return NULL;
}
SLIST_FOREACH(svc, &peer->svcs, next) { SLIST_FOREACH(svc, &peer->svcs, next) {
if (ble_uuid_cmp(&svc->svc.uuid.u, uuid) == 0) { if (ble_uuid_cmp(&svc->svc.uuid.u, uuid) == 0) {
return svc; return svc;
@@ -700,6 +715,10 @@ peer_chr_find_uuid(const struct peer *peer, const ble_uuid_t *svc_uuid,
const struct peer_svc *svc; const struct peer_svc *svc;
const struct peer_chr *chr; const struct peer_chr *chr;
if (chr_uuid == NULL) {
return NULL;
}
svc = peer_svc_find_uuid(peer, svc_uuid); svc = peer_svc_find_uuid(peer, svc_uuid);
if (svc == NULL) { if (svc == NULL) {
return NULL; return NULL;
@@ -721,6 +740,10 @@ peer_dsc_find_uuid(const struct peer *peer, const ble_uuid_t *svc_uuid,
const struct peer_chr *chr; const struct peer_chr *chr;
const struct peer_dsc *dsc; const struct peer_dsc *dsc;
if (dsc_uuid == NULL) {
return NULL;
}
chr = peer_chr_find_uuid(peer, svc_uuid, chr_uuid); chr = peer_chr_find_uuid(peer, svc_uuid, chr_uuid);
if (chr == NULL) { if (chr == NULL) {
return NULL; return NULL;
@@ -810,6 +833,7 @@ peer_svc_disced(uint16_t conn_handle, const struct ble_gatt_error *error,
#else #else
/* All services discovered; start discovering characteristics. */ /* All services discovered; start discovering characteristics. */
if (peer->disc_prev_chr_val > 0) { if (peer->disc_prev_chr_val > 0) {
peer->cur_svc = NULL;
peer_disc_chrs(peer); peer_disc_chrs(peer);
} }
#endif #endif
@@ -979,6 +1003,7 @@ peer_free_mem(void)
{ {
free(peer_mem); free(peer_mem);
peer_mem = NULL; peer_mem = NULL;
SLIST_INIT(&peers);
free(peer_svc_mem); free(peer_svc_mem);
peer_svc_mem = NULL; peer_svc_mem = NULL;
@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD * SPDX-FileCopyrightText: 2021-2026 Espressif Systems (Shanghai) CO LTD
* *
* SPDX-License-Identifier: Unlicense OR CC0-1.0 * SPDX-License-Identifier: Unlicense OR CC0-1.0
*/ */
@@ -20,23 +20,27 @@
static TaskHandle_t cli_task; static TaskHandle_t cli_task;
static QueueHandle_t cli_handle; static QueueHandle_t cli_handle;
static int stop; static volatile int stop;
static int enter_passkey_handler(int argc, char *argv[]) static int enter_passkey_handler(int argc, char *argv[])
{ {
int key; int key;
char pkey[8]; char pkey[8] = {0};
int num; int num;
if (argc != 2) { if (argc != 2) {
return -1; return -1;
} }
if (cli_handle == NULL) {
return -1;
}
sscanf(argv[1], "%7s", pkey); sscanf(argv[1], "%7s", pkey);
ESP_LOGI("You entered", "%s %s", argv[0], argv[1]); ESP_LOGI("You entered", "%s %s", argv[0], argv[1]);
num = pkey[0]; num = pkey[0];
if (isalpha(num)) { if (isalpha((unsigned char)num)) {
if ((strcasecmp(pkey, "Y") == 0) || (strcasecmp(pkey, "Yes") == 0)) { if ((strcasecmp(pkey, "Y") == 0) || (strcasecmp(pkey, "Yes") == 0)) {
key = 1; key = 1;
xQueueSend(cli_handle, &key, 0); xQueueSend(cli_handle, &key, 0);
@@ -56,6 +60,9 @@ static int enter_passkey_handler(int argc, char *argv[])
int scli_receive_key(int *console_key) int scli_receive_key(int *console_key)
{ {
if (cli_handle == NULL) {
return pdFALSE;
}
return xQueueReceive(cli_handle, console_key, BLE_RX_TIMEOUT); return xQueueReceive(cli_handle, console_key, BLE_RX_TIMEOUT);
} }
@@ -108,10 +115,8 @@ static void scli_task(void *arg)
} }
} }
if (event.type == UART_DATA) { if (event.type == UART_DATA) {
while (uart_read_bytes(uart_num, (uint8_t *) &linebuf[i], 1, 0)) { while (i < sizeof(linebuf) - 1 &&
if (i >= sizeof(linebuf) - 1) { uart_read_bytes(uart_num, (uint8_t *) &linebuf[i], 1, 0)) {
break;
}
if (linebuf[i] == '\r') { if (linebuf[i] == '\r') {
uart_write_bytes(uart_num, "\r\n", 2); uart_write_bytes(uart_num, "\r\n", 2);
} else { } else {
@@ -143,12 +148,14 @@ int scli_init(void)
/* Register CLI "key <value>" to accept input from user during pairing */ /* Register CLI "key <value>" to accept input from user during pairing */
ble_register_cli(); ble_register_cli();
xTaskCreate(scli_task, "scli_cli", 4096, (void *) 0, 3, &cli_task); cli_handle = xQueueCreate(1, sizeof(int));
if (cli_task == NULL) { if (cli_handle == NULL) {
return ESP_FAIL; return ESP_FAIL;
} }
cli_handle = xQueueCreate( 1, sizeof(int) ); xTaskCreate(scli_task, "scli_cli", 4096, (void *) 0, 3, &cli_task);
if (cli_handle == NULL) { if (cli_task == NULL) {
vQueueDelete(cli_handle);
cli_handle = NULL;
return ESP_FAIL; return ESP_FAIL;
} }
return ESP_OK; return ESP_OK;
@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD * SPDX-FileCopyrightText: 2024-2026 Espressif Systems (Shanghai) CO LTD
* *
* SPDX-License-Identifier: Unlicense OR CC0-1.0 * SPDX-License-Identifier: Unlicense OR CC0-1.0
*/ */
@@ -32,7 +32,7 @@ static int dtm_set_ble_tx_power_command(int argc, char **argv)
} }
ESP_LOGI(__func__, "Set tx power level '%d'", dtm_set_tx_power_cmd_args.cmd_params->ival[0]); ESP_LOGI(__func__, "Set tx power level '%d'", dtm_set_tx_power_cmd_args.cmd_params->ival[0]);
if (dtm_set_tx_power_cmd_args.cmd_params->ival[0] > 15) { if (dtm_set_tx_power_cmd_args.cmd_params->ival[0] < 0 || dtm_set_tx_power_cmd_args.cmd_params->ival[0] > 15) {
return 2; return 2;
} }
@@ -48,6 +48,11 @@ static int dtm_get_ble_tx_power_command(int argc, char **argv)
{ {
esp_power_level_t power_level; esp_power_level_t power_level;
if (esp_bt_controller_get_status() != ESP_BT_CONTROLLER_STATUS_ENABLED) {
esp_rom_printf("\nPlease enable BLE DTM mode first by using the command enable_ble_dtm -e 1 before sending this command.\n");
return 2;
}
power_level = esp_ble_tx_power_get_enhanced(ESP_BLE_ENHANCED_PWR_TYPE_DEFAULT, 0); power_level = esp_ble_tx_power_get_enhanced(ESP_BLE_ENHANCED_PWR_TYPE_DEFAULT, 0);
if (power_level == ESP_PWR_LVL_INVALID) { if (power_level == ESP_PWR_LVL_INVALID) {
@@ -67,10 +72,18 @@ static int dtm_reconfig_uart_pins_command(int argc, char **argv)
return 1; return 1;
} }
ESP_LOGI(__func__, "reconfig tx:'%d', rx: '%d'", int tx_pin = dtm_reconfig_uart_cmd_args.tx_pin->ival[0];
dtm_reconfig_uart_cmd_args.tx_pin->ival[0], dtm_reconfig_uart_cmd_args.rx_pin->ival[0]); int rx_pin = dtm_reconfig_uart_cmd_args.rx_pin->ival[0];
hci_uart_reconfig_pin(dtm_reconfig_uart_cmd_args.tx_pin->ival[0], if (tx_pin < 0 || rx_pin < 0) {
dtm_reconfig_uart_cmd_args.rx_pin->ival[0], -1, -1); ESP_LOGE(__func__, "Invalid GPIO pin: tx=%d, rx=%d", tx_pin, rx_pin);
return 1;
}
ESP_LOGI(__func__, "reconfig tx:'%d', rx: '%d'", tx_pin, rx_pin);
int rc = hci_uart_reconfig_pin(tx_pin, rx_pin, -1, -1);
if (rc != 0) {
ESP_LOGE(__func__, "Failed to reconfig UART pins; rc=%d", rc);
return 1;
}
return 0; return 0;
} }
@@ -151,9 +151,11 @@ gatt_svc_access(uint16_t conn_handle, uint16_t attr_handle,
sizeof(gatt_svr_chr_val), sizeof(gatt_svr_chr_val),
sizeof(gatt_svr_chr_val), sizeof(gatt_svr_chr_val),
&gatt_svr_chr_val, NULL); &gatt_svr_chr_val, NULL);
ble_gatts_chr_updated(attr_handle); if (rc == 0) {
MODLOG_DFLT(INFO, "Notification/Indication scheduled for " ble_gatts_chr_updated(attr_handle);
"all subscribed peers.\n"); MODLOG_DFLT(INFO, "Notification/Indication scheduled for "
"all subscribed peers.\n");
}
return rc; return rc;
} }
goto unknown; goto unknown;
@@ -170,7 +172,7 @@ gatt_svc_access(uint16_t conn_handle, uint16_t attr_handle,
if (ble_uuid_cmp(uuid, &gatt_svr_dsc_uuid.u) == 0) { if (ble_uuid_cmp(uuid, &gatt_svr_dsc_uuid.u) == 0) {
rc = os_mbuf_append(ctxt->om, rc = os_mbuf_append(ctxt->om,
&gatt_svr_dsc_val, &gatt_svr_dsc_val,
sizeof(gatt_svr_chr_val)); sizeof(gatt_svr_dsc_val));
return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES; return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
} }
goto unknown; goto unknown;
@@ -184,9 +184,10 @@ bleprph_advertise(void)
fields.name_is_complete = 1; fields.name_is_complete = 1;
#endif #endif
fields.uuids16 = (ble_uuid16_t[]) { static const ble_uuid16_t adv_uuids16[] = {
BLE_UUID16_INIT(GATT_SVR_SVC_ALERT_UUID) BLE_UUID16_INIT(GATT_SVR_SVC_ALERT_UUID)
}; };
fields.uuids16 = adv_uuids16;
fields.num_uuids16 = 1; fields.num_uuids16 = 1;
fields.uuids16_is_complete = 1; fields.uuids16_is_complete = 1;
@@ -480,17 +481,21 @@ bleprph_on_reset(int reason)
static void static void
ble_app_set_addr(void) ble_app_set_addr(void)
{ {
ble_addr_t addr; ble_addr_t addr = {0};
int rc; int rc;
/* generate new non-resolvable private address */ /* generate new non-resolvable private address */
rc = ble_hs_id_gen_rnd(0, &addr); rc = ble_hs_id_gen_rnd(0, &addr);
assert(rc == 0); if (rc != 0) {
MODLOG_DFLT(ERROR, "ble_hs_id_gen_rnd failed; rc=%d\n", rc);
return;
}
/* set generated address */ /* set generated address */
rc = ble_hs_id_set_rnd(addr.val); rc = ble_hs_id_set_rnd(addr.val);
if (rc != 0) {
assert(rc == 0); MODLOG_DFLT(ERROR, "ble_hs_id_set_rnd failed; rc=%d\n", rc);
}
} }
#endif #endif
@@ -507,8 +512,8 @@ bleprph_on_sync(void)
#if CONFIG_EXAMPLE_USE_CI_ADDRESS #if CONFIG_EXAMPLE_USE_CI_ADDRESS
if (strlen(CONFIG_EXAMPLE_CI_ADDRESS_OFFSET)) { if (strlen(CONFIG_EXAMPLE_CI_ADDRESS_OFFSET)) {
uint8_t addr[6] = {0}; uint8_t addr[6] = {0};
uint32_t *offset = (uint32_t *)&addr[1]; uint32_t offset_val = (uint32_t)atoi(CONFIG_EXAMPLE_CI_ADDRESS_OFFSET);
*offset = atoi(CONFIG_EXAMPLE_CI_ADDRESS_OFFSET); memcpy(&addr[1], &offset_val, sizeof(offset_val));
addr[5] = 0xC3; addr[5] = 0xC3;
addr[0] = CONFIG_IDF_FIRMWARE_CHIP_ID; addr[0] = CONFIG_IDF_FIRMWARE_CHIP_ID;
rc = ble_hs_id_set_rnd(addr); rc = ble_hs_id_set_rnd(addr);
@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2015-2025 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
*/ */
@@ -170,8 +170,10 @@ static void register_heap(void)
static int tasks_info(int argc, char **argv) static int tasks_info(int argc, char **argv)
{ {
const size_t bytes_per_task = 40; /* see vTaskList description */ /* Use a larger per-task estimate (80 bytes) plus a safety margin of 10 extra
char *task_list_buffer = malloc(uxTaskGetNumberOfTasks() * bytes_per_task); * slots to absorb TOCTOU races and SMP mode affinity fields. */
const size_t bytes_per_task = 80;
char *task_list_buffer = malloc((uxTaskGetNumberOfTasks() + 10) * bytes_per_task);
if (task_list_buffer == NULL) { if (task_list_buffer == NULL) {
ESP_LOGE(TAG, "failed to allocate buffer for vTaskList output"); ESP_LOGE(TAG, "failed to allocate buffer for vTaskList output");
return 1; return 1;
@@ -219,14 +221,19 @@ static int deep_sleep(int argc, char **argv)
return 1; return 1;
} }
if (deep_sleep_args.wakeup_time->count) { if (deep_sleep_args.wakeup_time->count) {
uint64_t timeout = 1000ULL * deep_sleep_args.wakeup_time->ival[0]; int wakeup_ms = deep_sleep_args.wakeup_time->ival[0];
if (wakeup_ms <= 0) {
ESP_LOGE(TAG, "Invalid wakeup time: %d ms (must be > 0)", wakeup_ms);
return 1;
}
uint64_t timeout = 1000ULL * wakeup_ms;
ESP_LOGI(TAG, "Enabling timer wakeup, timeout=%lluus", timeout); ESP_LOGI(TAG, "Enabling timer wakeup, timeout=%lluus", timeout);
ESP_ERROR_CHECK( esp_sleep_enable_timer_wakeup(timeout) ); ESP_ERROR_CHECK( esp_sleep_enable_timer_wakeup(timeout) );
} }
if (deep_sleep_args.wakeup_gpio_num->count) { if (deep_sleep_args.wakeup_gpio_num->count) {
int io_num = deep_sleep_args.wakeup_gpio_num->ival[0]; int io_num = deep_sleep_args.wakeup_gpio_num->ival[0];
if (!rtc_gpio_is_valid_gpio(io_num)) { if (io_num < 0 || !rtc_gpio_is_valid_gpio(io_num)) {
ESP_LOGE(TAG, "GPIO %d is not an RTC IO", io_num); ESP_LOGE(TAG, "GPIO %d is not a valid RTC IO", io_num);
return 1; return 1;
} }
int level = 0; int level = 0;
@@ -237,11 +244,13 @@ static int deep_sleep(int argc, char **argv)
return 1; return 1;
} }
} }
#if SOC_PM_SUPPORT_EXT1_WAKEUP
ESP_LOGI(TAG, "Enabling wakeup on GPIO%d, wakeup on %s level", ESP_LOGI(TAG, "Enabling wakeup on GPIO%d, wakeup on %s level",
io_num, level ? "HIGH" : "LOW"); io_num, level ? "HIGH" : "LOW");
#if SOC_PM_SUPPORT_EXT1_WAKEUP
ESP_ERROR_CHECK( esp_sleep_enable_ext1_wakeup_io(1ULL << io_num, level) ); ESP_ERROR_CHECK( esp_sleep_enable_ext1_wakeup_io(1ULL << io_num, level) );
#else
ESP_LOGE(TAG, "GPIO wakeup from deep sleep not supported on this target");
return 1;
#endif #endif
} }
@@ -311,22 +320,44 @@ static int light_sleep(int argc, char **argv)
ESP_LOGE(TAG, "Invalid wakeup level: %d", level); ESP_LOGE(TAG, "Invalid wakeup level: %d", level);
return 1; return 1;
} }
if (!GPIO_IS_VALID_GPIO(io_num)) {
ESP_LOGE(TAG, "Invalid GPIO number: %d", io_num);
return 1;
}
/* Configure the pin as input with a pull to avoid floating and false wakeups */
gpio_config_t io_conf = {
.pin_bit_mask = 1ULL << io_num,
.mode = GPIO_MODE_INPUT,
.pull_up_en = (level == 0) ? GPIO_PULLUP_ENABLE : GPIO_PULLUP_DISABLE,
.pull_down_en = (level == 1) ? GPIO_PULLDOWN_ENABLE : GPIO_PULLDOWN_DISABLE,
.intr_type = GPIO_INTR_DISABLE,
};
esp_err_t gpio_cfg_err = gpio_config(&io_conf);
if (gpio_cfg_err != ESP_OK) {
ESP_LOGE(TAG, "gpio_config failed for GPIO%d: %s", io_num, esp_err_to_name(gpio_cfg_err));
return 1;
}
ESP_LOGI(TAG, "Enabling wakeup on GPIO%d, wakeup on %s level", ESP_LOGI(TAG, "Enabling wakeup on GPIO%d, wakeup on %s level",
io_num, level ? "HIGH" : "LOW"); io_num, level ? "HIGH" : "LOW");
esp_err_t wakeup_err = gpio_wakeup_enable(io_num, level ? GPIO_INTR_HIGH_LEVEL : GPIO_INTR_LOW_LEVEL);
ESP_ERROR_CHECK( gpio_wakeup_enable(io_num, level ? GPIO_INTR_HIGH_LEVEL : GPIO_INTR_LOW_LEVEL) ); if (wakeup_err != ESP_OK) {
ESP_LOGE(TAG, "gpio_wakeup_enable failed for GPIO%d: %s", io_num, esp_err_to_name(wakeup_err));
return 1;
}
} }
if (io_count > 0) { if (io_count > 0) {
ESP_ERROR_CHECK( esp_sleep_enable_gpio_wakeup() ); ESP_ERROR_CHECK( esp_sleep_enable_gpio_wakeup() );
} }
if (CONFIG_ESP_CONSOLE_UART_NUM <= UART_NUM_1) { if (CONFIG_ESP_CONSOLE_UART_NUM >= 0 && CONFIG_ESP_CONSOLE_UART_NUM <= UART_NUM_1) {
ESP_LOGI(TAG, "Enabling UART wakeup (press ENTER to exit light sleep)"); ESP_LOGI(TAG, "Enabling UART wakeup (press ENTER to exit light sleep)");
uart_wakeup_cfg_t uart_wakeup_cfg = {.wakeup_mode = UART_WK_MODE_ACTIVE_THRESH, .rx_edge_threshold = 3}; uart_wakeup_cfg_t uart_wakeup_cfg = {.wakeup_mode = UART_WK_MODE_ACTIVE_THRESH, .rx_edge_threshold = 3};
ESP_ERROR_CHECK( uart_wakeup_setup(CONFIG_ESP_CONSOLE_UART_NUM, &uart_wakeup_cfg) ); ESP_ERROR_CHECK( uart_wakeup_setup(CONFIG_ESP_CONSOLE_UART_NUM, &uart_wakeup_cfg) );
ESP_ERROR_CHECK( esp_sleep_enable_uart_wakeup(CONFIG_ESP_CONSOLE_UART_NUM) ); ESP_ERROR_CHECK( esp_sleep_enable_uart_wakeup(CONFIG_ESP_CONSOLE_UART_NUM) );
} }
fflush(stdout); fflush(stdout);
uart_wait_tx_idle_polling(CONFIG_ESP_CONSOLE_UART_NUM); if (CONFIG_ESP_CONSOLE_UART_NUM >= 0) {
uart_wait_tx_idle_polling(CONFIG_ESP_CONSOLE_UART_NUM);
}
esp_light_sleep_start(); esp_light_sleep_start();
uint32_t causes = esp_sleep_get_wakeup_causes(); uint32_t causes = esp_sleep_get_wakeup_causes();
@@ -71,7 +71,7 @@ static uint16_t handle;
#define PHY_CODED_S8 3 #define PHY_CODED_S8 3
#if CONFIG_EXAMPLE_EXTENDED_ADV #if CONFIG_EXAMPLE_EXTENDED_ADV
static int current_phy_updated; static volatile int current_phy_updated;
#endif #endif
/* State for callback-chained read throughput test */ /* State for callback-chained read throughput test */
@@ -194,8 +194,9 @@ blecent_notify(uint16_t conn_handle, uint16_t val_handle,
return 0; return 0;
err: err:
/* Terminate the connection. */ /* Terminate the connection; return original error code so caller detects failure. */
return ble_gap_terminate(peer->conn_handle, BLE_ERR_REM_USER_CONN_TERM); ble_gap_terminate(peer->conn_handle, BLE_ERR_REM_USER_CONN_TERM);
return rc;
} }
static int blecent_write(uint16_t conn_handle, uint16_t val_handle, static int blecent_write(uint16_t conn_handle, uint16_t val_handle,
@@ -599,6 +600,11 @@ read_cleanup:
break; break;
case NOTIFY_THROUGHPUT: case NOTIFY_THROUGHPUT:
if (test_data[1] <= 0) {
ESP_LOGE(tag, "Please enter non-zero value for test time in seconds!!");
break;
}
if (test_data[2] == PHY_CODED_S2) { if (test_data[2] == PHY_CODED_S2) {
switch_conn_params(conn_handle, &conn_params_coded_s2); switch_conn_params(conn_handle, &conn_params_coded_s2);
} else if (test_data[2] == PHY_CODED_S8) { } else if (test_data[2] == PHY_CODED_S8) {
@@ -641,7 +647,7 @@ read_cleanup:
" can be seen on peripheral terminal after %d seconds", " can be seen on peripheral terminal after %d seconds",
test_data[1]); test_data[1]);
} }
vTaskDelay(test_data[1]*1000 / portTICK_PERIOD_MS); vTaskDelay((TickType_t)test_data[1] * 1000 / portTICK_PERIOD_MS);
/* Unsubscribe so the next notify test triggers a fresh /* Unsubscribe so the next notify test triggers a fresh
* BLE_GAP_EVENT_SUBSCRIBE on the peripheral (cur_notify 0→1) */ * BLE_GAP_EVENT_SUBSCRIBE on the peripheral (cur_notify 0→1) */
@@ -762,22 +768,17 @@ ext_blecent_should_connect(const struct ble_gap_ext_disc_desc *disc)
{ {
int offset = 0; int offset = 0;
int ad_struct_len = 0; int ad_struct_len = 0;
uint8_t test_addr[6];
uint8_t parsed_addr[6]; uint8_t parsed_addr[6];
uint8_t phy_uuid_found = 0; uint8_t phy_uuid_found = 0;
if (disc->legacy_event_type != BLE_HCI_ADV_RPT_EVTYPE_ADV_IND && if (!(disc->props & BLE_HCI_ADV_CONN_MASK)) {
disc->legacy_event_type != BLE_HCI_ADV_RPT_EVTYPE_DIR_IND) {
return 0; return 0;
} }
if (strlen(CONFIG_EXAMPLE_PEER_ADDR) && (strncmp(CONFIG_EXAMPLE_PEER_ADDR, "ADDR_ANY", strlen("ADDR_ANY")) != 0)) { if (strlen(CONFIG_EXAMPLE_PEER_ADDR) && (strncmp(CONFIG_EXAMPLE_PEER_ADDR, "ADDR_ANY", strlen("ADDR_ANY")) != 0)) {
// ESP_LOGI(tag, "Peer address from menuconfig: %s", CONFIG_EXAMPLE_PEER_ADDR); /* peer_addr_parse stores address in little-endian order matching disc->addr.val;
/* Convert string to address */ * no byte reversal needed. */
peer_addr_parse(CONFIG_EXAMPLE_PEER_ADDR, parsed_addr); peer_addr_parse(CONFIG_EXAMPLE_PEER_ADDR, parsed_addr);
for (int i = 0; i < 6; i++) { if (memcmp(parsed_addr, disc->addr.val, sizeof(disc->addr.val)) != 0) {
test_addr[5 - i] = parsed_addr[i];
}
if (memcmp(test_addr, disc->addr.val, sizeof(disc->addr.val)) != 0) {
return 0; return 0;
} }
} }
@@ -860,9 +861,13 @@ blecent_should_connect(const struct ble_gap_disc_desc *disc)
char serv_name[] = "nimble_prph"; char serv_name[] = "nimble_prph";
if (fields.name != NULL) { if (fields.name != NULL) {
ESP_LOGI(tag, "Device Name = %s", (char *)fields.name); /* fields.name is not null-terminated; use %.*s for safe printing */
ESP_LOGI(tag, "Device Name = %.*s", fields.name_len, (char *)fields.name);
if (memcmp(fields.name, serv_name, fields.name_len) == 0) { /* Require exact length match to prevent prefix false-positives and
* avoid reading past the end of serv_name (stack over-read). */
if (fields.name_len == (uint8_t)strlen(serv_name) &&
memcmp(fields.name, serv_name, fields.name_len) == 0) {
ESP_LOGI(tag, "central connect to `nimble_prph` success"); ESP_LOGI(tag, "central connect to `nimble_prph` success");
return 1; return 1;
} }
@@ -912,6 +917,9 @@ blecent_connect_if_interesting(void *disc)
rc = ble_hs_id_infer_auto(0, &own_addr_type); rc = ble_hs_id_infer_auto(0, &own_addr_type);
if (rc != 0) { if (rc != 0) {
ESP_LOGE(tag, "error determining address type; rc=%d", rc); ESP_LOGE(tag, "error determining address type; rc=%d", rc);
#if !(MYNEWT_VAL(BLE_HOST_ALLOW_CONNECT_WITH_SCAN))
blecent_scan();
#endif
return; return;
} }
@@ -924,6 +932,9 @@ blecent_connect_if_interesting(void *disc)
ESP_LOGE(tag, "Error: Failed to connect to device; addr_type=%d " ESP_LOGE(tag, "Error: Failed to connect to device; addr_type=%d "
"addr=%s; rc=%d\n", "addr=%s; rc=%d\n",
conn_addr.type, addr_str(conn_addr.val), rc); conn_addr.type, addr_str(conn_addr.val), rc);
#if !(MYNEWT_VAL(BLE_HOST_ALLOW_CONNECT_WITH_SCAN))
blecent_scan();
#endif
return; return;
} }
} }
@@ -1038,7 +1049,6 @@ blecent_gap_event(struct ble_gap_event *event, void *arg)
/* Forget about peer. */ /* Forget about peer. */
peer_delete(event->disconnect.conn.conn_handle); peer_delete(event->disconnect.conn.conn_handle);
vTaskDelay(200);
/* Resume scanning. */ /* Resume scanning. */
blecent_scan(); blecent_scan();
@@ -1212,9 +1222,11 @@ app_main(void)
assert(rc == 0); assert(rc == 0);
#endif #endif
#if CONFIG_BT_NIMBLE_GAP_SERVICE
/* Set the default device name. */ /* Set the default device name. */
rc = ble_svc_gap_device_name_set("gattc-throughput"); rc = ble_svc_gap_device_name_set("gattc-throughput");
assert(rc == 0); assert(rc == 0);
#endif
/* XXX Need to have template for store */ /* XXX Need to have template for store */
ble_store_config_init(); ble_store_config_init();
@@ -1241,32 +1253,9 @@ app_main(void)
printf(" | |\n"); printf(" | |\n");
printf("\n ===============================================================================================\n"); printf("\n ===============================================================================================\n");
const char *prompt = LOG_COLOR_I "Throughput demo >> " LOG_RESET_COLOR; /* Start the REPL task that was created (but left blocked) by
* esp_console_new_repl_uart. Without this call the task would wait
while (true) { * forever, leaking its 4 KB stack and TCB. */
/* Get a line using linenoise. ESP_ERROR_CHECK(esp_console_start_repl(repl));
* The line is returned when ENTER is pressed. /* app_main can now return; the REPL task handles console I/O from here. */
*/
char *line = linenoise(prompt);
if (line == NULL) { /* Ignore empty lines */
continue;
}
/* Add the command to the history */
linenoiseHistoryAdd(line);
/* Try to run the command */
int ret;
esp_err_t err = esp_console_run(line, &ret);
if (err == ESP_ERR_NOT_FOUND) {
printf("Unrecognized command\n");
} else if (err == ESP_ERR_INVALID_ARG) {
// command was empty
} else if (err == ESP_OK && ret != ESP_OK) {
printf("Command returned non-zero error code: 0x%x (%s)\n", ret, esp_err_to_name(ret));
} else if (err != ESP_OK) {
printf("Internal error: %s\n", esp_err_to_name(err));
}
/* linenoise allocates line buffer on the heap, so need to free it */
linenoiseFree(line);
}
} }
@@ -28,30 +28,44 @@ peer_addr_parse(const char *addr_str, uint8_t addr[PEER_ADDR_VAL_SIZE])
void void
print_bytes(const uint8_t *bytes, int len) print_bytes(const uint8_t *bytes, int len)
{ {
/* Build the entire hex string into a buffer first and log it in one call.
* Calling MODLOG_DFLT per byte adds a full log header and newline to each
* byte, breaking the intended colon-separated format and blocking the CPU. */
char buf[256];
int pos = 0;
int i; int i;
for (i = 0; i < len; i++) { for (i = 0; i < len && pos < (int)(sizeof(buf) - 5); i++) {
MODLOG_DFLT(DEBUG, "%s0x%02x", i != 0 ? ":" : "", bytes[i]); if (i != 0) {
buf[pos++] = ':';
}
pos += snprintf(buf + pos, sizeof(buf) - pos, "0x%02x", bytes[i]);
} }
buf[pos] = '\0';
MODLOG_DFLT(DEBUG, "%s", buf);
} }
void void
print_mbuf(const struct os_mbuf *om) print_mbuf(const struct os_mbuf *om)
{ {
int colon, i; char buf[512];
int pos = 0;
int colon = 0;
int i;
colon = 0; while (om != NULL && pos < (int)(sizeof(buf) - 6)) {
while (om != NULL) { for (i = 0; i < om->om_len && pos < (int)(sizeof(buf) - 6); i++) {
if (colon) { if (colon) {
MODLOG_DFLT(INFO, ":"); buf[pos++] = ':';
} else { } else {
colon = 1; colon = 1;
} }
for (i = 0; i < om->om_len; i++) { pos += snprintf(buf + pos, sizeof(buf) - pos, "0x%02x", om->om_data[i]);
MODLOG_DFLT(INFO, "%s0x%02x", i != 0 ? ":" : "", om->om_data[i]);
} }
om = SLIST_NEXT(om, om_next); om = SLIST_NEXT(om, om_next);
} }
buf[pos] = '\0';
MODLOG_DFLT(INFO, "%s", buf);
} }
char * char *
@@ -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
*/ */
@@ -40,7 +40,7 @@ chr_end_handle(const struct peer_svc *svc, const struct peer_chr *chr);
int int
chr_is_empty(const struct peer_svc *svc, const struct peer_chr *chr); chr_is_empty(const struct peer_svc *svc, const struct peer_chr *chr);
static struct peer_chr * static struct peer_chr *
peer_chr_find(const struct peer_svc *svc, uint16_t chr_def_handle, peer_chr_find(const struct peer_svc *svc, uint16_t chr_val_handle,
struct peer_chr **out_prev); struct peer_chr **out_prev);
static void static void
peer_disc_chrs(struct peer *peer); peer_disc_chrs(struct peer *peer);
@@ -168,7 +168,7 @@ peer_dsc_add(struct peer *peer, uint16_t chr_val_handle,
if (prev == NULL) { if (prev == NULL) {
SLIST_INSERT_HEAD(&chr->dscs, dsc, next); SLIST_INSERT_HEAD(&chr->dscs, dsc, next);
} else { } else {
SLIST_NEXT(prev, next) = dsc; SLIST_INSERT_AFTER(prev, dsc, next);
} }
return 0; return 0;
@@ -197,6 +197,7 @@ peer_disc_dscs(struct peer *peer)
peer_dsc_disced, peer); peer_dsc_disced, peer);
if (rc != 0) { if (rc != 0) {
peer_disc_complete(peer, rc); peer_disc_complete(peer, rc);
return;
} }
peer->disc_prev_chr_val = chr->chr.val_handle; peer->disc_prev_chr_val = chr->chr.val_handle;
@@ -340,7 +341,7 @@ peer_chr_add(struct peer *peer, uint16_t svc_start_handle,
return BLE_HS_EUNKNOWN; return BLE_HS_EUNKNOWN;
} }
chr = peer_chr_find(svc, gatt_chr->def_handle, &prev); chr = peer_chr_find(svc, gatt_chr->val_handle, &prev);
if (chr != NULL) { if (chr != NULL) {
/* Characteristic already discovered. */ /* Characteristic already discovered. */
return 0; return 0;
@@ -408,13 +409,20 @@ peer_disc_chrs(struct peer *peer)
struct peer_svc *svc; struct peer_svc *svc;
int rc; int rc;
/* Search through the list of discovered service for the first service that /* Advance cur_svc to the next service to discover characteristics for.
* contains undiscovered characteristics. Then, discover all * Starting from NULL means start from the first service in the list.
* characteristics belonging to that service. * This sequential approach prevents re-processing services that had no
*/ * characteristics (where SLIST_EMPTY would remain true and cause an
* infinite loop with the old SLIST_FOREACH-from-start approach). */
if (peer->cur_svc == NULL) {
peer->cur_svc = SLIST_FIRST(&peer->svcs);
} else {
peer->cur_svc = SLIST_NEXT(peer->cur_svc, next);
}
SLIST_FOREACH(svc, &peer->svcs, next) { /* Scan forward past empty services to find the next one to process */
if (!peer_svc_is_empty(svc) && SLIST_EMPTY(&svc->chrs)) { for (svc = peer->cur_svc; svc != NULL; svc = SLIST_NEXT(svc, next)) {
if (!peer_svc_is_empty(svc)) {
peer->cur_svc = svc; peer->cur_svc = svc;
rc = ble_gattc_disc_all_chrs(peer->conn_handle, rc = ble_gattc_disc_all_chrs(peer->conn_handle,
svc->svc.start_handle, svc->svc.start_handle,
@@ -600,9 +608,10 @@ peer_disc_incs(struct peer *peer)
peer->cur_svc = SLIST_NEXT(peer->cur_svc, next); peer->cur_svc = SLIST_NEXT(peer->cur_svc, next);
if (peer->cur_svc == NULL) { if (peer->cur_svc == NULL) {
if (peer->disc_prev_chr_val > 0) { if (peer->disc_prev_chr_val > 0) {
/* cur_svc is already NULL: peer_disc_chrs starts from first svc */
peer_disc_chrs(peer); peer_disc_chrs(peer);
return;
} }
return; /* Always return; don't fall through into svc processing */
} }
} }
@@ -613,10 +622,18 @@ peer_disc_incs(struct peer *peer)
svc->svc.end_handle, svc->svc.end_handle,
peer_inc_disced, peer); peer_inc_disced, peer);
if (rc != 0) { if (rc != 0) {
/* Hard error: skip to characteristic discovery from beginning */
peer->cur_svc = NULL;
peer_disc_chrs(peer); peer_disc_chrs(peer);
} }
} else if (svc != NULL) {
/* svc is empty: advance to next service for include discovery */
peer_disc_incs(peer);
} else { } else {
peer_disc_chrs(peer); /* cur_svc is NULL on entry: no services at all */
if (peer->disc_prev_chr_val > 0) {
peer_disc_chrs(peer);
}
} }
} }
#endif #endif
@@ -690,8 +707,12 @@ peer_svc_find_uuid(const struct peer *peer, const ble_uuid_t *uuid)
{ {
const struct peer_svc *svc; const struct peer_svc *svc;
if (peer == NULL || uuid == NULL) {
return NULL;
}
SLIST_FOREACH(svc, &peer->svcs, next) { SLIST_FOREACH(svc, &peer->svcs, next) {
if ((uuid != NULL) && (ble_uuid_cmp(&(svc->svc.uuid.u), uuid) == 0)) { if (ble_uuid_cmp(&(svc->svc.uuid.u), uuid) == 0) {
return svc; return svc;
} }
} }
@@ -727,6 +748,10 @@ peer_dsc_find_uuid(const struct peer *peer, const ble_uuid_t *svc_uuid,
const struct peer_chr *chr; const struct peer_chr *chr;
const struct peer_dsc *dsc; const struct peer_dsc *dsc;
if (peer == NULL) {
return NULL;
}
chr = peer_chr_find_uuid(peer, svc_uuid, chr_uuid); chr = peer_chr_find_uuid(peer, svc_uuid, chr_uuid);
if (chr == NULL) { if (chr == NULL) {
return NULL; return NULL;
@@ -816,6 +841,7 @@ peer_svc_disced(uint16_t conn_handle, const struct ble_gatt_error *error,
#else #else
/* All services discovered; start discovering characteristics. */ /* All services discovered; start discovering characteristics. */
if (peer->disc_prev_chr_val > 0) { if (peer->disc_prev_chr_val > 0) {
peer->cur_svc = NULL;
peer_disc_chrs(peer); peer_disc_chrs(peer);
} }
#endif #endif
@@ -920,6 +946,8 @@ peer_add(uint16_t conn_handle)
static void static void
peer_free_mem(void) peer_free_mem(void)
{ {
SLIST_INIT(&peers);
free(peer_mem); free(peer_mem);
peer_mem = NULL; peer_mem = NULL;
@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2015-2025 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
*/ */
@@ -102,7 +102,7 @@ static int conn_mtu_handler(int argc, char *argv[])
static int throughput_demo_handler(int argc, char *argv[]) static int throughput_demo_handler(int argc, char *argv[])
{ {
char pkey[8]; char pkey[8] = {0};
struct cli_msg msg = { struct cli_msg msg = {
.type = CLI_MSG_TYPE_THROUGHPUT, .type = CLI_MSG_TYPE_THROUGHPUT,
}; };
@@ -111,7 +111,15 @@ static int throughput_demo_handler(int argc, char *argv[])
return -1; return -1;
} }
sscanf(argv[1], "%7s", pkey); if (cli_handle == NULL) {
ESP_LOGE("CLI", "Queue not initialized");
return -1;
}
if (sscanf(argv[1], "%7s", pkey) != 1) {
ESP_LOGE("CLI", "Failed to parse throughput type");
return -1;
}
if (strcmp(pkey, "read") == 0) { if (strcmp(pkey, "read") == 0) {
msg.data.key[0] = 1; msg.data.key[0] = 1;
@@ -133,14 +141,17 @@ static int throughput_demo_handler(int argc, char *argv[])
static int yesno_handler(int argc, char *argv[]) static int yesno_handler(int argc, char *argv[])
{ {
char yesno[4]; char yesno[4] = {0};
bool yes; bool yes;
if (argc != 2) { if (argc != 2) {
return -1; return -1;
} }
sscanf(argv[1], "%3s", yesno); if (sscanf(argv[1], "%3s", yesno) != 1) {
ESP_LOGE("CLI", "Failed to parse yes/no argument");
return -1;
}
if (strcmp(yesno, "Yes") == 0 || strcmp(yesno, "YES") == 0 || strcmp(yesno, "yes") == 0) { if (strcmp(yesno, "Yes") == 0 || strcmp(yesno, "YES") == 0 || strcmp(yesno, "yes") == 0) {
yes = 1; yes = 1;
@@ -151,23 +162,29 @@ static int yesno_handler(int argc, char *argv[])
} }
ESP_LOGI("User entered", "%s %s", argv[0], yesno); ESP_LOGI("User entered", "%s %s", argv[0], yesno);
/* Send as 24-byte buffer to match queue item size */ /* Use struct cli_msg to exactly match the queue item size (sizeof(struct cli_msg))
uint8_t yesno_buf[24] = {0}; * and avoid stack buffer over-read from sending undersized raw buffers. */
yesno_buf[0] = (uint8_t)yes; struct cli_msg msg = {
.type = CLI_MSG_TYPE_YESNO,
.data.yes = yes,
};
if (cli_handle) { if (cli_handle) {
xQueueSend(cli_handle, yesno_buf, 500 / portTICK_PERIOD_MS); xQueueSend(cli_handle, &msg, 500 / portTICK_PERIOD_MS);
} }
return 0; return 0;
} }
int scli_receive_yesno(bool *console_key) int scli_receive_yesno(bool *console_key)
{ {
/* Receive into temporary 24-byte buffer to match queue item size, struct cli_msg msg;
* then extract bool value to prevent buffer overflow */ int ret;
uint8_t temp_buf[24];
int ret = xQueueReceive(cli_handle, temp_buf, YES_NO_PARAM); if (cli_handle == NULL) {
return pdFALSE;
}
ret = xQueueReceive(cli_handle, &msg, YES_NO_PARAM);
if (ret == pdPASS) { if (ret == pdPASS) {
*console_key = (bool)temp_buf[0]; /* Extract first byte as bool */ *console_key = msg.data.yes;
} }
return ret; return ret;
} }
@@ -175,6 +192,9 @@ int scli_receive_yesno(bool *console_key)
int scli_receive_key(int console_key[6]) int scli_receive_key(int console_key[6])
{ {
struct cli_msg msg; struct cli_msg msg;
if (cli_handle == NULL) {
return 0;
}
if (xQueueReceive(cli_handle, &msg, BLE_RX_PARAM) != pdTRUE) { if (xQueueReceive(cli_handle, &msg, BLE_RX_PARAM) != pdTRUE) {
return 0; return 0;
} }
@@ -198,6 +218,9 @@ int scli_receive_key(int console_key[6])
int cli_receive_key(int console_key[6]) int cli_receive_key(int console_key[6])
{ {
struct cli_msg msg; struct cli_msg msg;
if (cli_handle == NULL) {
return 0;
}
if (xQueueReceive(cli_handle, &msg, BLE_RX_TIMEOUT) != pdTRUE) { if (xQueueReceive(cli_handle, &msg, BLE_RX_TIMEOUT) != pdTRUE) {
return 0; return 0;
} }
@@ -68,10 +68,17 @@ ext_get_data(uint8_t ext_adv_pattern[], int size)
int rc; int rc;
data = os_msys_get_pkthdr(size, 0); data = os_msys_get_pkthdr(size, 0);
assert(data); if (!data) {
ESP_LOGE(tag, "ext_get_data: mbuf alloc failed");
return NULL;
}
rc = os_mbuf_append(data, ext_adv_pattern, size); rc = os_mbuf_append(data, ext_adv_pattern, size);
assert(rc == 0); if (rc != 0) {
ESP_LOGE(tag, "ext_get_data: mbuf_append failed; rc=%d", rc);
os_mbuf_free_chain(data);
return NULL;
}
return data; return data;
} }
@@ -147,13 +154,16 @@ ext_bleprph_advertise(void)
/*enable connectable advertising for all Phy*/ /*enable connectable advertising for all Phy*/
params.connectable = 1; params.connectable = 1;
/* advertise using random addr */ /* Use dynamically inferred address type (set in gatts_on_sync via ble_hs_id_infer_auto) */
params.own_addr_type = BLE_OWN_ADDR_PUBLIC; params.own_addr_type = gatts_addr_type;
/* Set current phy; get mbuf for scan rsp data; fill mbuf with scan rsp data */ /* Set current phy; get mbuf for scan rsp data; fill mbuf with scan rsp data */
params.primary_phy = BLE_HCI_LE_PHY_1M_PREF_MASK ; params.primary_phy = BLE_HCI_LE_PHY_1M_PREF_MASK ;
params.secondary_phy = BLE_HCI_LE_PHY_2M_PREF_MASK ; params.secondary_phy = BLE_HCI_LE_PHY_2M_PREF_MASK ;
data = ext_get_data(ext_adv_pattern, sizeof(ext_adv_pattern)); data = ext_get_data(ext_adv_pattern, sizeof(ext_adv_pattern));
if (!data) {
return;
}
params.sid = 0; params.sid = 0;
params.itvl_min = BLE_GAP_ADV_FAST_INTERVAL1_MIN; params.itvl_min = BLE_GAP_ADV_FAST_INTERVAL1_MIN;
@@ -282,7 +292,11 @@ notify_task(void *arg)
/* Memory not available for mbuf, yield briefly */ /* Memory not available for mbuf, yield briefly */
vTaskDelay(1); vTaskDelay(1);
} }
} while (om == NULL); } while (om == NULL && notify_state);
if (om == NULL) {
/* notify_state went false while waiting; stop test */
break;
}
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) {
@@ -348,12 +362,14 @@ gatts_gap_event(struct ble_gap_event *event, void *arg)
} }
if (event->connect.status != 0) { if (event->connect.status != 0) {
/* Connection failed; resume advertising */ /* Connection failed; resume advertising. Skip HCI/conn_handle
* updates since the connection handle is invalid on failure. */
#if CONFIG_EXAMPLE_EXTENDED_ADV #if CONFIG_EXAMPLE_EXTENDED_ADV
ext_bleprph_advertise(); ext_bleprph_advertise();
#else #else
gatts_advertise(); gatts_advertise();
#endif #endif
break;
} }
rc = ble_hs_hci_util_set_data_len(event->connect.conn_handle, rc = ble_hs_hci_util_set_data_len(event->connect.conn_handle,
@@ -419,10 +435,15 @@ gatts_gap_event(struct ble_gap_event *event, void *arg)
} }
} else { } else {
ESP_LOGI(tag, "Notifications disabled"); ESP_LOGI(tag, "Notifications disabled");
/* Wake up notify_task so it can exit the test loop cleanly;
* without this the task would block forever on ulTaskNotifyTake. */
if (notify_task_handle) {
xTaskNotifyGive(notify_task_handle);
}
} }
} else if (event->subscribe.attr_handle != notify_handle) {
notify_state = event->subscribe.cur_notify;
} }
/* Do NOT modify notify_state for other characteristics: that would
* corrupt the throughput test state while it is running. */
break; break;
case BLE_GAP_EVENT_NOTIFY_TX: case BLE_GAP_EVENT_NOTIFY_TX:
@@ -533,7 +554,8 @@ void app_main(void)
BaseType_t task_rc = xTaskCreate(notify_task, "notify_task", 4096, NULL, 10, &notify_task_handle); BaseType_t task_rc = xTaskCreate(notify_task, "notify_task", 4096, NULL, 10, &notify_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 ; nimble_port_deinit();
return;
} }
#if MYNEWT_VAL(BLE_GATTS) #if MYNEWT_VAL(BLE_GATTS)
rc = gatt_svr_init(); rc = gatt_svr_init();
@@ -22,7 +22,7 @@
static const char *TAG = "l2cap_coc_cent"; static const char *TAG = "l2cap_coc_cent";
#define L2CAP_COC_PSM 0x1002 #define L2CAP_COC_PSM 0x0080 /* valid dynamic LE L2CAP CoC PSM (0x0080-0x00FF) */
#define L2CAP_COC_MTU CONFIG_EXAMPLE_L2CAP_COC_MTU #define L2CAP_COC_MTU CONFIG_EXAMPLE_L2CAP_COC_MTU
#define COC_BUF_COUNT (6 * MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM)) #define COC_BUF_COUNT (6 * MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM))
/* Block size must include mbuf headers so each SDU fits in one pool entry. */ /* Block size must include mbuf headers so each SDU fits in one pool entry. */
@@ -130,12 +130,9 @@ static void cent_l2cap_coc_connect(uint16_t conn_handle)
if (rc != 0) { if (rc != 0) {
ESP_LOGE(TAG, "L2CAP COC connect failed; rc=%d", rc); ESP_LOGE(TAG, "L2CAP COC connect failed; rc=%d", rc);
l2cap_connecting = false; l2cap_connecting = false;
/* EINVAL: NimBLE returns before chan alloc, sdu_rx not consumed — free it. /* NimBLE takes ownership of sdu_rx on ALL error paths from ble_l2cap_connect
* ENOTCONN: NimBLE frees sdu_rx on all ENOTCONN paths (early !conn check * (freed via ble_l2cap_chan_free → ble_l2cap_coc_cleanup_chan or directly in
* and late TX failure via ble_l2cap_coc_cleanup_chan). Do not free here. */ * ble_l2cap_sig_coc_connect validation). Never free here to avoid double-free. */
if (rc == BLE_HS_EINVAL) {
os_mbuf_free_chain(sdu_rx);
}
} }
} }
@@ -713,6 +710,8 @@ void app_main(void)
ret = nimble_port_init(); ret = nimble_port_init();
if (ret != ESP_OK) { if (ret != ESP_OK) {
ESP_LOGE(TAG, "nimble_port_init failed; rc=%d", ret); ESP_LOGE(TAG, "nimble_port_init failed; rc=%d", ret);
vEventGroupDelete(coc_event_group);
coc_event_group = NULL;
return; return;
} }
@@ -731,6 +730,9 @@ void app_main(void)
if (xTaskCreate(cent_send_task, "cent_send_task", 4096, NULL, 5, NULL) != pdPASS) { if (xTaskCreate(cent_send_task, "cent_send_task", 4096, NULL, 5, NULL) != pdPASS) {
ESP_LOGE(TAG, "Failed to create cent_send_task"); ESP_LOGE(TAG, "Failed to create cent_send_task");
nimble_port_deinit();
vEventGroupDelete(coc_event_group);
coc_event_group = NULL;
return; return;
} }
@@ -20,7 +20,7 @@
static const char *TAG = "l2cap_coc_prph"; static const char *TAG = "l2cap_coc_prph";
#define L2CAP_COC_PSM 0x1002 #define L2CAP_COC_PSM 0x0080 /* valid dynamic LE L2CAP CoC PSM (0x0080-0x00FF) */
#define L2CAP_COC_MTU CONFIG_EXAMPLE_L2CAP_COC_MTU #define L2CAP_COC_MTU CONFIG_EXAMPLE_L2CAP_COC_MTU
#define COC_BUF_COUNT (6 * MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM)) #define COC_BUF_COUNT (6 * MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM))
/* Block size must include mbuf headers so each SDU fits in one pool entry. */ /* Block size must include mbuf headers so each SDU fits in one pool entry. */
@@ -149,7 +149,11 @@ static void prph_advertise(void)
fields.name_len = strlen(name); fields.name_len = strlen(name);
fields.name_is_complete = 1; fields.name_is_complete = 1;
#endif #endif
fields.uuids16 = (ble_uuid16_t[]){ BLE_UUID16_INIT(L2CAP_COC_UUID) }; /* Must be static: ble_gap_adv_set_fields copies the pointer, not the data.
* A stack compound literal becomes dangling after prph_advertise returns,
* causing corruption when BLE_NIMBLE_ENABLE_CONN_REATTEMPT re-uses the pointer. */
static const ble_uuid16_t adv_uuids16[] = { BLE_UUID16_INIT(L2CAP_COC_UUID) };
fields.uuids16 = adv_uuids16;
fields.num_uuids16 = 1; fields.num_uuids16 = 1;
fields.uuids16_is_complete = 1; fields.uuids16_is_complete = 1;
@@ -188,7 +192,11 @@ static int prph_l2cap_coc_accept(struct ble_l2cap_chan *chan)
return BLE_HS_ENOMEM; return BLE_HS_ENOMEM;
} }
int rc = ble_l2cap_recv_ready(chan, sdu_rx); int rc = ble_l2cap_recv_ready(chan, sdu_rx);
if (rc != 0) { /* ble_l2cap_coc_recv_ready stores sdu_rx AFTER the BLE_HS_EBUSY check but
* BEFORE the BLE_HS_ENOENT check. On BLE_HS_EBUSY the buffer was never
* stored and must be freed here; on success or BLE_HS_ENOENT the buffer is
* owned by chan->coc_rx.sdus[] and freed by ble_l2cap_coc_cleanup_chan. */
if (rc != 0 && rc != BLE_HS_ENOENT) {
os_mbuf_free_chain(sdu_rx); os_mbuf_free_chain(sdu_rx);
} }
return rc; return rc;