mirror of
https://github.com/espressif/esp-idf.git
synced 2025-07-29 18:27:20 +02:00
Merge branch 'feat/pts_changes_v5.2' into 'release/v5.2'
feat(nimble): BLE 5.4 PTS Related Features and Fixes (v5.2) See merge request espressif/esp-idf!34973
This commit is contained in:
@ -744,6 +744,7 @@ if(CONFIG_BT_ENABLED)
|
||||
"host/nimble/nimble/nimble/host/store/config/src/ble_store_nvs.c"
|
||||
"host/nimble/nimble/nimble/host/src/ble_gattc_cache.c"
|
||||
"host/nimble/nimble/nimble/host/src/ble_gattc_cache_conn.c"
|
||||
"host/nimble/nimble/nimble/host/src/ble_eatt.c"
|
||||
)
|
||||
|
||||
if(CONFIG_BT_CONTROLLER_DISABLED AND CONFIG_BT_NIMBLE_TRANSPORT_UART)
|
||||
|
@ -100,6 +100,14 @@ config BT_NIMBLE_L2CAP_COC_MAX_NUM
|
||||
help
|
||||
Defines maximum number of BLE Connection Oriented Channels. When set to (0), BLE COC is not compiled in
|
||||
|
||||
config BT_NIMBLE_L2CAP_ENHANCED_COC
|
||||
bool "L2CAP Enhanced Connection Oriented Channel"
|
||||
depends on BT_NIMBLE_ENABLED && (BT_NIMBLE_L2CAP_COC_MAX_NUM >= 1)
|
||||
default 0
|
||||
help
|
||||
Enable Enhanced Credit Based Flow Control Mode
|
||||
|
||||
|
||||
choice BT_NIMBLE_PINNED_TO_CORE_CHOICE
|
||||
prompt "The CPU core on which NimBLE host will run"
|
||||
depends on BT_NIMBLE_ENABLED && !FREERTOS_UNICORE
|
||||
@ -222,6 +230,13 @@ config BT_NIMBLE_SM_SC_LVL
|
||||
3. Authenticated pairing with encryption
|
||||
4. Authenticated LE Secure Connections pairing with encryption using a 128-bit strength encryption key.
|
||||
|
||||
config BT_NIMBLE_SM_SC_ONLY
|
||||
int "Enable Secure Connections Only Mode"
|
||||
depends on BT_NIMBLE_SECURITY_ENABLE
|
||||
default 0
|
||||
help
|
||||
Enable Secure Connections Only Mode
|
||||
|
||||
config BT_NIMBLE_DEBUG
|
||||
bool "Enable extra runtime asserts and host debugging"
|
||||
default n
|
||||
@ -611,6 +626,13 @@ if BT_NIMBLE_50_FEATURE_SUPPORT
|
||||
default y
|
||||
help
|
||||
This enables controller transfer periodic sync events to host
|
||||
|
||||
config BT_NIMBLE_PERIODIC_ADV_WITH_RESPONSES
|
||||
bool "Enable Periodic Advertisement with Response (EXPERIMENTAL)"
|
||||
depends on BT_NIMBLE_ENABLE_PERIODIC_ADV
|
||||
default n
|
||||
help
|
||||
This enables controller PAwR (Periodic Advertisement with Response).
|
||||
endif
|
||||
|
||||
config BT_NIMBLE_EXT_SCAN
|
||||
@ -685,8 +707,16 @@ if BT_NIMBLE_50_FEATURE_SUPPORT
|
||||
default 64
|
||||
help
|
||||
Set this option to set the upper limit on number of descriptors per connection to be cached.
|
||||
config BT_NIMBLE_GATT_CACHING_DISABLE_AUTO
|
||||
bool "Do not start discovery procedure automatically upon receiving Out of Sync"
|
||||
depends on BT_NIMBLE_GATT_CACHING
|
||||
default n
|
||||
help
|
||||
When client receives ATT out-of-sync error message, it will not automatically start the discovery procedure
|
||||
to correct the invalid cache.
|
||||
endif
|
||||
|
||||
|
||||
config BT_NIMBLE_WHITELIST_SIZE
|
||||
int "BLE white list size"
|
||||
depends on BT_NIMBLE_ENABLED
|
||||
@ -883,6 +913,12 @@ menu "GAP Service"
|
||||
Peripheral Preferred Connection Parameter: Supervision Timeout
|
||||
Timeout = Value * 10 ms
|
||||
|
||||
config BT_NIMBLE_SVC_GAP_GATT_SECURITY_LEVEL
|
||||
bool "LE GATT Security Level Characteristic"
|
||||
default n
|
||||
help
|
||||
Enable the LE GATT Security Level Characteristic
|
||||
|
||||
endmenu
|
||||
|
||||
menu "BLE Services"
|
||||
@ -906,6 +942,71 @@ menu "BLE Services"
|
||||
default 3
|
||||
help
|
||||
Defines maximum number of report characteristics per service instance
|
||||
|
||||
config BT_NIMBLE_SVC_BAS_BATTERY_LEVEL_NOTIFY
|
||||
depends on BT_NIMBLE_ENABLED
|
||||
bool "BAS Battery Level NOTIFY permission"
|
||||
default n
|
||||
help
|
||||
Enable/Disable notifications on BAS Battery Level Characteristic
|
||||
|
||||
menu "Device Information Service"
|
||||
config BT_NIMBLE_SVC_DIS_MANUFACTURER_NAME
|
||||
depends on BT_NIMBLE_ENABLED
|
||||
bool "Manufacturer Name"
|
||||
default n
|
||||
help
|
||||
Enable the DIS characteristic Manufacturer Name String characteristic
|
||||
|
||||
config BT_NIMBLE_SVC_DIS_SERIAL_NUMBER
|
||||
depends on BT_NIMBLE_ENABLED
|
||||
bool "Serial Number"
|
||||
default n
|
||||
help
|
||||
Enable the DIS Serial Number characteristic
|
||||
|
||||
config BT_NIMBLE_SVC_DIS_HARDWARE_REVISION
|
||||
depends on BT_NIMBLE_ENABLED
|
||||
bool "Hardware Revision"
|
||||
default n
|
||||
help
|
||||
Enable the DIS Hardware Revision characteristic
|
||||
|
||||
config BT_NIMBLE_SVC_DIS_FIRMWARE_REVISION
|
||||
depends on BT_NIMBLE_ENABLED
|
||||
bool "Firmware Revision"
|
||||
default n
|
||||
help
|
||||
Enable the DIS Firmware Revision characteristic
|
||||
|
||||
config BT_NIMBLE_SVC_DIS_SOFTWARE_REVISION
|
||||
depends on BT_NIMBLE_ENABLED
|
||||
bool "Software Revision"
|
||||
default n
|
||||
help
|
||||
Enable the DIS Software Revision characteristic
|
||||
|
||||
config BT_NIMBLE_SVC_DIS_SYSTEM_ID
|
||||
depends on BT_NIMBLE_ENABLED
|
||||
bool "System ID"
|
||||
default n
|
||||
help
|
||||
Enable the DIS System ID characteristic
|
||||
|
||||
config BT_NIMBLE_SVC_DIS_PNP_ID
|
||||
depends on BT_NIMBLE_ENABLED
|
||||
bool "PnP ID"
|
||||
default n
|
||||
help
|
||||
Enable the DIS PnP ID characteristic
|
||||
|
||||
config BT_NIMBLE_SVC_DIS_INCLUDED
|
||||
depends on BT_NIMBLE_ENABLED
|
||||
bool "DIS as an Included Service"
|
||||
default n
|
||||
help
|
||||
Use DIS as an included service
|
||||
endmenu
|
||||
endmenu
|
||||
|
||||
config BT_NIMBLE_VS_SUPPORT
|
||||
@ -926,7 +1027,6 @@ config BT_NIMBLE_OPTIMIZE_MULTI_CONN
|
||||
|
||||
config BT_NIMBLE_ENC_ADV_DATA
|
||||
bool "Encrypted Advertising Data"
|
||||
select BT_NIMBLE_EXT_ADV
|
||||
help
|
||||
This option is used to enable encrypted advertising data.
|
||||
|
||||
@ -1074,3 +1174,17 @@ menu "Host-controller Transport"
|
||||
help
|
||||
UART HCI CTS pin
|
||||
endmenu
|
||||
|
||||
config BT_NIMBLE_EATT_CHAN_NUM
|
||||
int "Maximum number of EATT channels"
|
||||
default 0
|
||||
depends on BT_NIMBLE_ENABLED
|
||||
help
|
||||
Defines the number of channels EATT bearers can use
|
||||
|
||||
config BT_NIMBLE_SUBRATE
|
||||
bool "Enable Subrate Change"
|
||||
default n
|
||||
depends on BT_NIMBLE_ENABLED
|
||||
help
|
||||
Enable connection subrate change feature
|
||||
|
Submodule components/bt/host/nimble/nimble updated: 2cb8439dd6...2cff20291d
@ -158,6 +158,16 @@
|
||||
#define MYNEWT_VAL_BLE_GATT_CACHING_MAX_DSCS (0)
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_BT_NIMBLE_GATT_CACHING_DISABLE_AUTO
|
||||
#define MYNEWT_VAL_BLE_GATT_CACHING_DISABLE_AUTO (CONFIG_BT_NIMBLE_GATT_CACHING_DISABLE_AUTO)
|
||||
#else
|
||||
#define MYNEWT_VAL_BLE_GATT_CACHING_DISABLE_AUTO (0)
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#ifndef MYNEWT_VAL_BLE_GATT_CSFC_SIZE
|
||||
#define MYNEWT_VAL_BLE_GATT_CSFC_SIZE (1)
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_BT_NIMBLE_MAX_EXT_ADV_INSTANCES
|
||||
@ -507,6 +517,12 @@
|
||||
|
||||
/*** @apache-mynewt-nimble/nimble/host */
|
||||
|
||||
#if CONFIG_BT_NIMBLE_L2CAP_ENHANCED_COC || CONFIG_BT_NIMBLE_EATT_CHAN_NUM
|
||||
#define MYNEWT_VAL_BLE_L2CAP_ENHANCED_COC (1)
|
||||
#else
|
||||
#define MYNEWT_VAL_BLE_L2CAP_ENHANCED_COC (0)
|
||||
#endif
|
||||
|
||||
#ifndef MYNEWT_VAL_BLE_DYNAMIC_SERVICE
|
||||
#ifdef CONFIG_BT_NIMBLE_DYNAMIC_SERVICE
|
||||
#define MYNEWT_VAL_BLE_DYNAMIC_SERVICE CONFIG_BT_NIMBLE_DYNAMIC_SERVICE
|
||||
@ -539,6 +555,10 @@
|
||||
#define MYNEWT_VAL_BLE_ATT_SVR_NOTIFY (1)
|
||||
#endif
|
||||
|
||||
#ifndef MYNEWT_VAL_BLE_ATT_SVR_NOTIFY_MULTI
|
||||
#define MYNEWT_VAL_BLE_ATT_SVR_NOTIFY_MULTI (1)
|
||||
#endif
|
||||
|
||||
#ifndef MYNEWT_VAL_BLE_ATT_SVR_QUEUED_WRITE
|
||||
#define MYNEWT_VAL_BLE_ATT_SVR_QUEUED_WRITE (1)
|
||||
#endif
|
||||
@ -563,6 +583,10 @@
|
||||
#define MYNEWT_VAL_BLE_ATT_SVR_READ_MULT (1)
|
||||
#endif
|
||||
|
||||
#ifndef MYNEWT_VAL_BLE_ATT_SVR_READ_MULT_VAR
|
||||
#define MYNEWT_VAL_BLE_ATT_SVR_READ_MULT_VAR (1)
|
||||
#endif
|
||||
|
||||
#ifndef MYNEWT_VAL_BLE_ATT_SVR_READ_TYPE
|
||||
#define MYNEWT_VAL_BLE_ATT_SVR_READ_TYPE (1)
|
||||
#endif
|
||||
@ -579,6 +603,49 @@
|
||||
#define MYNEWT_VAL_BLE_ATT_SVR_WRITE_NO_RSP (1)
|
||||
#endif
|
||||
|
||||
#ifndef MYNEWT_VAL_BLE_EATT_CHAN_NUM
|
||||
#define MYNEWT_VAL_BLE_EATT_CHAN_NUM (CONFIG_BT_NIMBLE_EATT_CHAN_NUM)
|
||||
#endif
|
||||
|
||||
#ifndef MYNEWT_VAL_BLE_EATT_LOG_LVL
|
||||
#define MYNEWT_VAL_BLE_EATT_LOG_LVL (1)
|
||||
#endif
|
||||
|
||||
#ifndef MYNEWT_VAL_BLE_EATT_LOG_MOD
|
||||
#define MYNEWT_VAL_BLE_EATT_LOG_MOD (27)
|
||||
#endif
|
||||
|
||||
#ifndef MYNEWT_VAL_BLE_EATT_MTU
|
||||
#define MYNEWT_VAL_BLE_EATT_MTU (128)
|
||||
#endif
|
||||
|
||||
#ifndef MYNEWT_VAL_BLE_CLIENT_SUPPORTED_FEATURES
|
||||
|
||||
#if MYNEWT_VAL_BLE_GATT_CACHING
|
||||
#define MYNEWT_VAL_BLE_CLIENT_SUPPORTED_FEATURES_ROBUST_CACHING (1)
|
||||
#else
|
||||
#define MYNEWT_VAL_BLE_CLIENT_SUPPORTED_FEATURES_ROBUST_CACHING (0)
|
||||
#endif //MYNEWT_VAL_BLE_GATT_CACHING
|
||||
|
||||
#if CONFIG_BT_NIMBLE_EATT_CHAN_NUM
|
||||
#define MYNEWT_VAL_BLE_CLIENT_SUPPORTED_FEATURES_EATT (2)
|
||||
#else
|
||||
#define MYNEWT_VAL_BLE_CLIENT_SUPPORTED_FEATURES_EATT (0)
|
||||
#endif //CONFIG_BT_NIMBLE_EATT_CHAN_NUM
|
||||
|
||||
#if MYNEWT_VAL_BLE_ATT_SVR_NOTIFY_MULTI
|
||||
#define MYNEWT_VAL_BLE_CLIENT_SUPPORTED_FEATURES_NOTIFY_MULTI (4)
|
||||
#else
|
||||
#define MYNEWT_VAL_BLE_CLIENT_SUPPORTED_FEATURES_NOTIFY_MULTI (0)
|
||||
#endif //MYNEWT_VAL_BLE_ATT_SVR_NOTIFY_MULTI
|
||||
|
||||
#define MYNEWT_VAL_BLE_CLIENT_SUPPORTED_FEATURES ( \
|
||||
MYNEWT_VAL_BLE_CLIENT_SUPPORTED_FEATURES_ROBUST_CACHING | \
|
||||
MYNEWT_VAL_BLE_CLIENT_SUPPORTED_FEATURES_EATT | \
|
||||
MYNEWT_VAL_BLE_CLIENT_SUPPORTED_FEATURES_NOTIFY_MULTI \
|
||||
)
|
||||
#endif //MYNEWT_VAL_CLIENT_SUPPORTED_FEATURES
|
||||
|
||||
#ifndef MYNEWT_VAL_BLE_GAP_MAX_PENDING_CONN_PARAM_UPDATE
|
||||
#define MYNEWT_VAL_BLE_GAP_MAX_PENDING_CONN_PARAM_UPDATE (1)
|
||||
#endif
|
||||
@ -619,6 +686,10 @@
|
||||
#define MYNEWT_VAL_BLE_GATT_NOTIFY (1)
|
||||
#endif
|
||||
|
||||
#ifndef MYNEWT_VAL_BLE_GATT_NOTIFY_MULTIPLE
|
||||
#define MYNEWT_VAL_BLE_GATT_NOTIFY_MULTIPLE (1)
|
||||
#endif
|
||||
|
||||
#ifndef MYNEWT_VAL_BLE_GATT_READ
|
||||
#define MYNEWT_VAL_BLE_GATT_READ (MYNEWT_VAL_BLE_ROLE_CENTRAL)
|
||||
#endif
|
||||
@ -745,11 +816,15 @@
|
||||
#define MYNEWT_VAL_BLE_HS_SYSINIT_STAGE (200)
|
||||
#endif
|
||||
|
||||
#if CONFIG_BT_NIMBLE_EATT_CHAN_NUM > CONFIG_BT_NIMBLE_L2CAP_COC_MAX_NUM
|
||||
#define MYNEWT_VAL_BLE_L2CAP_COC_MAX_NUM (CONFIG_BT_NIMBLE_EATT_CHAN_NUM)
|
||||
#else
|
||||
#ifndef CONFIG_BT_NIMBLE_L2CAP_COC_MAX_NUM
|
||||
#define MYNEWT_VAL_BLE_L2CAP_COC_MAX_NUM (2)
|
||||
#else
|
||||
#define MYNEWT_VAL_BLE_L2CAP_COC_MAX_NUM CONFIG_BT_NIMBLE_L2CAP_COC_MAX_NUM
|
||||
#endif
|
||||
#endif //CONFIG_BT_NIMBLE_L2CAP_COC_MAX_NUM
|
||||
#endif //CONFIG_BT_NIMBLE_EATT_CHAN_NUM
|
||||
|
||||
#ifndef MYNEWT_VAL_BLE_L2CAP_COC_MPS
|
||||
#define MYNEWT_VAL_BLE_L2CAP_COC_MPS (MYNEWT_VAL_MSYS_1_BLOCK_SIZE-8)
|
||||
@ -767,7 +842,11 @@
|
||||
#define MYNEWT_VAL_BLE_L2CAP_RX_FRAG_TIMEOUT (30000)
|
||||
#endif
|
||||
|
||||
#ifndef MYNEWT_VAL_BLE_L2CAP_SIG_MAX_PROCS
|
||||
#if CONFIG_BT_NIMBLE_EATT_CHAN_NUM > CONFIG_BT_NIMBLE_L2CAP_COC_MAX_NUM
|
||||
#define MYNEWT_VAL_BLE_L2CAP_SIG_MAX_PROCS (CONFIG_BT_NIMBLE_EATT_CHAN_NUM)
|
||||
#elif CONFIG_BT_NIMBLE_L2CAP_COC_MAX_NUM
|
||||
#define MYNEWT_VAL_BLE_L2CAP_SIG_MAX_PROCS (CONFIG_BT_NIMBLE_L2CAP_COC_MAX_NUM)
|
||||
#else
|
||||
#define MYNEWT_VAL_BLE_L2CAP_SIG_MAX_PROCS (1)
|
||||
#endif
|
||||
|
||||
@ -881,8 +960,12 @@
|
||||
#endif
|
||||
|
||||
#ifndef MYNEWT_VAL_BLE_SM_SC_ONLY
|
||||
#ifdef CONFIG_BT_NIMBLE_SM_SC_ONLY
|
||||
#define MYNEWT_VAL_BLE_SM_SC_ONLY (CONFIG_BT_NIMBLE_SM_SC_ONLY)
|
||||
#else
|
||||
#define MYNEWT_VAL_BLE_SM_SC_ONLY (0)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
#ifndef MYNEWT_VAL_BLE_SM_THEIR_KEY_DIST
|
||||
@ -909,6 +992,10 @@
|
||||
#define MYNEWT_VAL_BLE_STORE_MAX_CCCDS CONFIG_BT_NIMBLE_MAX_CCCDS
|
||||
#endif
|
||||
|
||||
#ifndef MYNEWT_VAL_BLE_STORE_MAX_CSFCS
|
||||
#define MYNEWT_VAL_BLE_STORE_MAX_CSFCS CONFIG_BT_NIMBLE_MAX_BONDS
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_BT_NIMBLE_MAX_EADS
|
||||
#define MYNEWT_VAL_BLE_STORE_MAX_EADS CONFIG_BT_NIMBLE_MAX_EADS
|
||||
#endif
|
||||
@ -953,7 +1040,7 @@
|
||||
|
||||
/*** nimble/host/services/bas */
|
||||
#ifndef MYNEWT_VAL_BLE_SVC_BAS_BATTERY_LEVEL_NOTIFY_ENABLE
|
||||
#define MYNEWT_VAL_BLE_SVC_BAS_BATTERY_LEVEL_NOTIFY_ENABLE (1)
|
||||
#define MYNEWT_VAL_BLE_SVC_BAS_BATTERY_LEVEL_NOTIFY_ENABLE (CONFIG_BT_NIMBLE_SVC_BAS_BATTERY_LEVEL_NOTIFY)
|
||||
#endif
|
||||
|
||||
#ifndef MYNEWT_VAL_BLE_SVC_BAS_BATTERY_LEVEL_READ_PERM
|
||||
@ -1553,7 +1640,7 @@
|
||||
|
||||
/*** @apache-mynewt-nimble/nimble/host/services/bas */
|
||||
#ifndef MYNEWT_VAL_BLE_SVC_BAS_BATTERY_LEVEL_NOTIFY_ENABLE
|
||||
#define MYNEWT_VAL_BLE_SVC_BAS_BATTERY_LEVEL_NOTIFY_ENABLE (1)
|
||||
#define MYNEWT_VAL_BLE_SVC_BAS_BATTERY_LEVEL_NOTIFY_ENABLE (CONFIG_BT_NIMBLE_SVC_BAS_BATTERY_LEVEL_NOTIFY)
|
||||
#endif
|
||||
|
||||
#ifndef MYNEWT_VAL_BLE_SVC_BAS_BATTERY_LEVEL_READ_PERM
|
||||
@ -1570,29 +1657,34 @@
|
||||
#endif
|
||||
|
||||
#ifndef MYNEWT_VAL_BLE_SVC_DIS_FIRMWARE_REVISION_DEFAULT
|
||||
#define MYNEWT_VAL_BLE_SVC_DIS_FIRMWARE_REVISION_DEFAULT (NULL)
|
||||
#define MYNEWT_VAL_BLE_SVC_DIS_FIRMWARE_REVISION_DEFAULT ("0000")
|
||||
#endif
|
||||
|
||||
/* Value copied from BLE_SVC_DIS_DEFAULT_READ_PERM */
|
||||
#ifndef MYNEWT_VAL_BLE_SVC_DIS_FIRMWARE_REVISION_READ_PERM
|
||||
#if CONFIG_BT_NIMBLE_SVC_DIS_FIRMWARE_REVISION
|
||||
#define MYNEWT_VAL_BLE_SVC_DIS_FIRMWARE_REVISION_READ_PERM (0)
|
||||
#else
|
||||
#define MYNEWT_VAL_BLE_SVC_DIS_FIRMWARE_REVISION_READ_PERM (-1)
|
||||
#endif
|
||||
|
||||
#ifndef MYNEWT_VAL_BLE_SVC_DIS_HARDWARE_REVISION_DEFAULT
|
||||
#define MYNEWT_VAL_BLE_SVC_DIS_HARDWARE_REVISION_DEFAULT (NULL)
|
||||
#define MYNEWT_VAL_BLE_SVC_DIS_HARDWARE_REVISION_DEFAULT ("0000")
|
||||
#endif
|
||||
|
||||
/* Value copied from BLE_SVC_DIS_DEFAULT_READ_PERM */
|
||||
#ifndef MYNEWT_VAL_BLE_SVC_DIS_HARDWARE_REVISION_READ_PERM
|
||||
#if CONFIG_BT_NIMBLE_SVC_DIS_HARDWARE_REVISION
|
||||
#define MYNEWT_VAL_BLE_SVC_DIS_HARDWARE_REVISION_READ_PERM (0)
|
||||
#else
|
||||
#define MYNEWT_VAL_BLE_SVC_DIS_HARDWARE_REVISION_READ_PERM (-1)
|
||||
#endif
|
||||
|
||||
#ifndef MYNEWT_VAL_BLE_SVC_DIS_MANUFACTURER_NAME_DEFAULT
|
||||
#define MYNEWT_VAL_BLE_SVC_DIS_MANUFACTURER_NAME_DEFAULT (NULL)
|
||||
#define MYNEWT_VAL_BLE_SVC_DIS_MANUFACTURER_NAME_DEFAULT ("espressif")
|
||||
#endif
|
||||
|
||||
/* Value copied from BLE_SVC_DIS_DEFAULT_READ_PERM */
|
||||
#ifndef MYNEWT_VAL_BLE_SVC_DIS_MANUFACTURER_NAME_READ_PERM
|
||||
#if CONFIG_BT_NIMBLE_SVC_DIS_MANUFACTURER_NAME
|
||||
#define MYNEWT_VAL_BLE_SVC_DIS_MANUFACTURER_NAME_READ_PERM (0)
|
||||
#else
|
||||
#define MYNEWT_VAL_BLE_SVC_DIS_MANUFACTURER_NAME_READ_PERM (-1)
|
||||
#endif
|
||||
|
||||
@ -1605,20 +1697,24 @@
|
||||
#endif
|
||||
|
||||
#ifndef MYNEWT_VAL_BLE_SVC_DIS_SERIAL_NUMBER_DEFAULT
|
||||
#define MYNEWT_VAL_BLE_SVC_DIS_SERIAL_NUMBER_DEFAULT (NULL)
|
||||
#define MYNEWT_VAL_BLE_SVC_DIS_SERIAL_NUMBER_DEFAULT ("0000")
|
||||
#endif
|
||||
|
||||
/* Value copied from BLE_SVC_DIS_DEFAULT_READ_PERM */
|
||||
#ifndef MYNEWT_VAL_BLE_SVC_DIS_SERIAL_NUMBER_READ_PERM
|
||||
#if CONFIG_BT_NIMBLE_SVC_DIS_SERIAL_NUMBER
|
||||
#define MYNEWT_VAL_BLE_SVC_DIS_SERIAL_NUMBER_READ_PERM (0)
|
||||
#else
|
||||
#define MYNEWT_VAL_BLE_SVC_DIS_SERIAL_NUMBER_READ_PERM (-1)
|
||||
#endif
|
||||
|
||||
#ifndef MYNEWT_VAL_BLE_SVC_DIS_SOFTWARE_REVISION_DEFAULT
|
||||
#define MYNEWT_VAL_BLE_SVC_DIS_SOFTWARE_REVISION_DEFAULT (NULL)
|
||||
#define MYNEWT_VAL_BLE_SVC_DIS_SOFTWARE_REVISION_DEFAULT ("0000")
|
||||
#endif
|
||||
|
||||
/* Value copied from BLE_SVC_DIS_DEFAULT_READ_PERM */
|
||||
#ifndef MYNEWT_VAL_BLE_SVC_DIS_SOFTWARE_REVISION_READ_PERM
|
||||
#if CONFIG_BT_NIMBLE_SVC_DIS_SOFTWARE_REVISION
|
||||
#define MYNEWT_VAL_BLE_SVC_DIS_SOFTWARE_REVISION_READ_PERM (0)
|
||||
#else
|
||||
#define MYNEWT_VAL_BLE_SVC_DIS_SOFTWARE_REVISION_READ_PERM (-1)
|
||||
#endif
|
||||
|
||||
@ -1627,23 +1723,31 @@
|
||||
#endif
|
||||
|
||||
#ifndef MYNEWT_VAL_BLE_SVC_DIS_SYSTEM_ID_DEFAULT
|
||||
#define MYNEWT_VAL_BLE_SVC_DIS_SYSTEM_ID_DEFAULT (NULL)
|
||||
#define MYNEWT_VAL_BLE_SVC_DIS_SYSTEM_ID_DEFAULT ("00000000")
|
||||
#endif
|
||||
|
||||
/* Value copied from BLE_SVC_DIS_DEFAULT_READ_PERM */
|
||||
#ifndef MYNEWT_VAL_BLE_SVC_DIS_SYSTEM_ID_READ_PERM
|
||||
#if CONFIG_BT_NIMBLE_SVC_DIS_SYSTEM_ID
|
||||
#define MYNEWT_VAL_BLE_SVC_DIS_SYSTEM_ID_READ_PERM (0)
|
||||
#else
|
||||
#define MYNEWT_VAL_BLE_SVC_DIS_SYSTEM_ID_READ_PERM (-1)
|
||||
#endif
|
||||
|
||||
#ifndef MYNEWT_VAL_BLE_SVC_DIS_PNP_ID_DEFAULT
|
||||
#define MYNEWT_VAL_BLE_SVC_DIS_PNP_ID_DEFAULT (NULL)
|
||||
#define MYNEWT_VAL_BLE_SVC_DIS_PNP_ID_DEFAULT ("000000")
|
||||
#endif
|
||||
|
||||
/* Value copied from BLE_SVC_DIS_DEFAULT_READ_PERM */
|
||||
#ifndef MYNEWT_VAL_BLE_SVC_DIS_PNP_ID_READ_PERM
|
||||
#if CONFIG_BT_NIMBLE_SVC_DIS_PNP_ID
|
||||
#define MYNEWT_VAL_BLE_SVC_DIS_PNP_ID_READ_PERM (0)
|
||||
#else
|
||||
#define MYNEWT_VAL_BLE_SVC_DIS_PNP_ID_READ_PERM (-1)
|
||||
#endif
|
||||
|
||||
#ifndef MYNEWT_VAL_BLE_SVC_DIS_INCLUDED
|
||||
#define MYNEWT_VAL_BLE_SVC_DIS_INCLUDED (CONFIG_BT_NIMBLE_SVC_DIS_INCLUDED)
|
||||
#endif
|
||||
|
||||
/*** @apache-mynewt-nimble/nimble/host/services/gap */
|
||||
#ifndef MYNEWT_VAL_BLE_SVC_GAP_APPEARANCE
|
||||
#define MYNEWT_VAL_BLE_SVC_GAP_APPEARANCE CONFIG_BT_NIMBLE_SVC_GAP_APPEARANCE
|
||||
@ -1706,6 +1810,11 @@
|
||||
CONFIG_BT_NIMBLE_SVC_GAP_PPCP_SUPERVISION_TMO
|
||||
#endif
|
||||
|
||||
#ifndef MYNEWT_VAL_BLE_SVC_GAP_GATT_SECURITY_LEVEL
|
||||
#define MYNEWT_VAL_BLE_SVC_GAP_GATT_SECURITY_LEVEL \
|
||||
CONFIG_BT_NIMBLE_SVC_GAP_GATT_SECURITY_LEVEL
|
||||
#endif
|
||||
|
||||
/*** nimble/transport */
|
||||
#ifndef MYNEWT_VAL_BLE_HCI_TRANSPORT_EMSPI
|
||||
#define MYNEWT_VAL_BLE_HCI_TRANSPORT_EMSPI (0)
|
||||
@ -1883,5 +1992,12 @@
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef MYNEWT_VAL_BLE_PERIODIC_ADV_WITH_RESPONSES
|
||||
#ifdef CONFIG_BT_NIMBLE_PERIODIC_ADV_WITH_RESPONSES
|
||||
#define MYNEWT_VAL_BLE_PERIODIC_ADV_WITH_RESPONSES (CONFIG_BT_NIMBLE_PERIODIC_ADV_WITH_RESPONSES)
|
||||
#else
|
||||
#define MYNEWT_VAL_BLE_PERIODIC_ADV_WITH_RESPONSES (0)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
@ -201,6 +201,16 @@ examples/bluetooth/nimble/ble_multi_conn:
|
||||
depends_filepatterns:
|
||||
- examples/bluetooth/nimble/common/**/*
|
||||
|
||||
examples/bluetooth/nimble/ble_pawr_adv:
|
||||
<<: *bt_default_depends
|
||||
enable:
|
||||
- if: IDF_TARGET == "esp32c6"
|
||||
|
||||
examples/bluetooth/nimble/ble_pawr_adv_conn:
|
||||
<<: *bt_default_depends
|
||||
enable:
|
||||
- if: IDF_TARGET == "esp32c6"
|
||||
|
||||
examples/bluetooth/nimble/ble_periodic_adv:
|
||||
<<: *bt_default_depends
|
||||
enable:
|
||||
|
@ -261,8 +261,11 @@ enc_adv_data_cent_decrypt(uint8_t length_data, const uint8_t *data, const uint8_
|
||||
|
||||
dec_data_len = temp[0];
|
||||
|
||||
MODLOG_DFLT(DEBUG, "Data after decryption:");
|
||||
print_bytes(temp, dec_data_len);
|
||||
MODLOG_DFLT(INFO, "Data after decryption:");
|
||||
for (int i = 0; i < dec_data_len + 1; i++) {
|
||||
MODLOG_DFLT(INFO, "0x%02X ", temp[i]);
|
||||
}
|
||||
MODLOG_DFLT(INFO, "\n");
|
||||
return 1;
|
||||
|
||||
default:
|
||||
@ -279,13 +282,19 @@ enc_adv_data_cent_decrypt(uint8_t length_data, const uint8_t *data, const uint8_
|
||||
* advertises connectability and support for the Key Characteristic service.
|
||||
*/
|
||||
static int
|
||||
ext_enc_adv_data_cent_should_connect(const struct ble_gap_ext_disc_desc *disc)
|
||||
enc_adv_data_cent_should_connect(const struct ble_gap_disc_desc *disc)
|
||||
{
|
||||
int offset = 0;
|
||||
int ad_struct_len = 0;
|
||||
struct ble_hs_adv_fields fields;
|
||||
int rc;
|
||||
int i;
|
||||
|
||||
if (disc->legacy_event_type != BLE_HCI_ADV_RPT_EVTYPE_ADV_IND &&
|
||||
disc->legacy_event_type != BLE_HCI_ADV_RPT_EVTYPE_DIR_IND) {
|
||||
if (disc->event_type != BLE_HCI_ADV_RPT_EVTYPE_ADV_IND &&
|
||||
disc->event_type != BLE_HCI_ADV_RPT_EVTYPE_DIR_IND) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
rc = ble_hs_adv_parse_fields(&fields, disc->data, disc->length_data);
|
||||
if (rc != 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -302,41 +311,31 @@ ext_enc_adv_data_cent_should_connect(const struct ble_gap_ext_disc_desc *disc)
|
||||
|
||||
/* The device has to advertise support for the Key Characteristic
|
||||
* service (0x2B88)
|
||||
*
|
||||
* Check if custom UUID 0x2C01 is advertised
|
||||
*/
|
||||
do {
|
||||
ad_struct_len = disc->data[offset];
|
||||
for (i = 0; i < fields.num_uuids16; i++) {
|
||||
if (ble_uuid_u16(&fields.uuids16[i].u) == 0x2C01) {
|
||||
if (enc_adv_data_find_peer(disc->addr.val) != -1) {
|
||||
MODLOG_DFLT(INFO, "Peer was already added with addr : %s",
|
||||
addr_str(&disc->addr.val));
|
||||
} else {
|
||||
MODLOG_DFLT(INFO, "Adding peer addr : %s", addr_str(&disc->addr.val));
|
||||
|
||||
if (!ad_struct_len) {
|
||||
break;
|
||||
}
|
||||
memcpy(&kmp[counter].peer_addr, &disc->addr.val, PEER_ADDR_VAL_SIZE);
|
||||
counter++;
|
||||
|
||||
/* Search if custom service UUID (0x2C01) is advertised */
|
||||
if (disc->data[offset] == 0x03 && disc->data[offset + 1] == 0x03) {
|
||||
if ( disc->data[offset + 2] == 0x2C && disc->data[offset + 3] == 0x01 ) {
|
||||
if (enc_adv_data_find_peer(disc->addr.val) != -1) {
|
||||
MODLOG_DFLT(INFO, "Peer was already added with addr : %s",
|
||||
addr_str(&disc->addr.val));
|
||||
} else {
|
||||
MODLOG_DFLT(INFO, "Adding peer addr : %s", addr_str(&disc->addr.val));
|
||||
|
||||
memcpy(&kmp[counter].peer_addr, &disc->addr.val, PEER_ADDR_VAL_SIZE);
|
||||
counter++;
|
||||
|
||||
if (counter > CONFIG_BT_NIMBLE_MAX_CONNECTIONS) {
|
||||
counter = 0;
|
||||
}
|
||||
}
|
||||
if (enc_adv_data_check_km_exist(disc->addr.val)) {
|
||||
return enc_adv_data_cent_decrypt(disc->length_data, disc->data, disc->addr.val);
|
||||
} else {
|
||||
return 1;
|
||||
if (counter > CONFIG_BT_NIMBLE_MAX_CONNECTIONS) {
|
||||
counter = 0;
|
||||
}
|
||||
}
|
||||
if (enc_adv_data_check_km_exist(disc->addr.val)) {
|
||||
return enc_adv_data_cent_decrypt(disc->length_data, disc->data, disc->addr.val);
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
offset += ad_struct_len + 1;
|
||||
|
||||
} while ( offset < disc->length_data );
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -354,7 +353,7 @@ enc_adv_data_cent_connect_if_interesting(void *disc)
|
||||
ble_addr_t *addr;
|
||||
|
||||
/* Don't do anything if we don't care about this advertiser. */
|
||||
if (!ext_enc_adv_data_cent_should_connect((struct ble_gap_ext_disc_desc *)disc)) {
|
||||
if (!enc_adv_data_cent_should_connect((struct ble_gap_disc_desc *)disc)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -377,7 +376,7 @@ enc_adv_data_cent_connect_if_interesting(void *disc)
|
||||
/* Try to connect the the advertiser. Allow 30 seconds (30000 ms) for
|
||||
* timeout.
|
||||
*/
|
||||
addr = &((struct ble_gap_ext_disc_desc *)disc)->addr;
|
||||
addr = &((struct ble_gap_disc_desc *)disc)->addr;
|
||||
|
||||
rc = ble_gap_connect(own_addr_type, addr, 30000, NULL,
|
||||
enc_adv_data_cent_gap_event, NULL);
|
||||
@ -459,13 +458,16 @@ enc_adv_data_cent_gap_event(struct ble_gap_event *event, void *arg)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Perform service discovery */
|
||||
rc = peer_disc_all(event->connect.conn_handle,
|
||||
enc_adv_data_cent_on_disc_complete, NULL);
|
||||
/** Authorization is required for this characterisitc */
|
||||
rc = ble_gap_security_initiate(event->connect.conn_handle);
|
||||
if (rc != 0) {
|
||||
MODLOG_DFLT(ERROR, "Failed to discover services; rc=%d\n", rc);
|
||||
return 0;
|
||||
MODLOG_DFLT(INFO, "Security could not be initiated, rc = %d\n", rc);
|
||||
return ble_gap_terminate(event->connect.conn_handle,
|
||||
BLE_ERR_REM_USER_CONN_TERM);
|
||||
} else {
|
||||
MODLOG_DFLT(INFO, "Connection secured\n");
|
||||
}
|
||||
|
||||
} else {
|
||||
/* Connection attempt failed; resume scanning. */
|
||||
MODLOG_DFLT(ERROR, "Error: Connection failed; status=%d\n",
|
||||
@ -493,6 +495,21 @@ enc_adv_data_cent_gap_event(struct ble_gap_event *event, void *arg)
|
||||
event->disc_complete.reason);
|
||||
return 0;
|
||||
|
||||
case BLE_GAP_EVENT_ENC_CHANGE:
|
||||
MODLOG_DFLT(INFO, "encryption change event; status=%d ",
|
||||
event->enc_change.status);
|
||||
rc = ble_gap_conn_find(event->enc_change.conn_handle, &desc);
|
||||
assert(rc == 0);
|
||||
print_conn_desc(&desc);
|
||||
|
||||
/* Perform service discovery */
|
||||
rc = peer_disc_all(event->enc_change.conn_handle,
|
||||
enc_adv_data_cent_on_disc_complete, NULL);
|
||||
if (rc != 0) {
|
||||
MODLOG_DFLT(ERROR, "Failed to discover services; rc=%d\n", rc);
|
||||
}
|
||||
return 0;
|
||||
|
||||
case BLE_GAP_EVENT_NOTIFY_RX:
|
||||
/* Peer sent us a notification or indication. */
|
||||
MODLOG_DFLT(INFO, "received %s; conn_handle=%d attr_handle=%d "
|
||||
@ -515,11 +532,25 @@ enc_adv_data_cent_gap_event(struct ble_gap_event *event, void *arg)
|
||||
event->mtu.value);
|
||||
return 0;
|
||||
|
||||
#if MYNEWT_VAL(BLE_EXT_ADV)
|
||||
case BLE_GAP_EVENT_EXT_DISC:
|
||||
/* An advertisement report was received during GAP discovery. */
|
||||
ext_print_adv_report(&event->disc);
|
||||
return 0;
|
||||
#endif
|
||||
|
||||
case BLE_GAP_EVENT_PASSKEY_ACTION:
|
||||
ESP_LOGI(tag, "PASSKEY_ACTION_EVENT started %d", event->passkey.params.action);
|
||||
struct ble_sm_io pkey = {0};
|
||||
|
||||
if (event->passkey.params.action == BLE_SM_IOACT_INPUT) {
|
||||
pkey.action = event->passkey.params.action;
|
||||
pkey.passkey = 123456;
|
||||
ESP_LOGI(tag, "Entering passkey %" PRIu32, pkey.passkey);
|
||||
rc = ble_sm_inject_io(event->passkey.conn_handle, &pkey);
|
||||
ESP_LOGI(tag, "ble_sm_inject_io result: %d", rc);
|
||||
}
|
||||
|
||||
enc_adv_data_cent_connect_if_interesting(&event->disc);
|
||||
return 0;
|
||||
|
||||
default:
|
||||
@ -577,12 +608,16 @@ app_main(void)
|
||||
ble_hs_cfg.sync_cb = enc_adv_data_cent_on_sync;
|
||||
ble_hs_cfg.store_status_cb = ble_store_util_status_rr;
|
||||
|
||||
/** This feature requires authentication */
|
||||
ble_hs_cfg.sm_mitm = 1;
|
||||
ble_hs_cfg.sm_io_cap = BLE_SM_IO_CAP_KEYBOARD_ONLY;
|
||||
|
||||
/* Initialize data structures to track connected peers. */
|
||||
rc = peer_init(MYNEWT_VAL(BLE_MAX_CONNECTIONS), 64, 64, 64);
|
||||
assert(rc == 0);
|
||||
|
||||
/* Set the default device name. */
|
||||
rc = ble_svc_gap_device_name_set("nimble-enc_adv_data_cent");
|
||||
rc = ble_svc_gap_device_name_set("enc_adv_data_cent");
|
||||
assert(rc == 0);
|
||||
|
||||
/* XXX Need to have template for store */
|
||||
|
@ -10,5 +10,5 @@ CONFIG_BTDM_CTRL_MODE_BR_EDR_ONLY=n
|
||||
CONFIG_BTDM_CTRL_MODE_BTDM=n
|
||||
CONFIG_BT_BLUEDROID_ENABLED=n
|
||||
CONFIG_BT_NIMBLE_ENABLED=y
|
||||
CONFIG_BT_NIMBLE_EXT_ADV=y
|
||||
CONFIG_BT_NIMBLE_EXT_ADV=n
|
||||
CONFIG_BT_NIMBLE_50_FEATURE_SUPPORT=y
|
||||
|
@ -2,7 +2,7 @@ menu "Example Configuration"
|
||||
|
||||
choice EXAMPLE_USE_IO_TYPE
|
||||
prompt "I/O Capability"
|
||||
default BLE_SM_IO_CAP_NO_IO
|
||||
default BLE_SM_IO_CAP_DISP_ONLY
|
||||
help
|
||||
I/O capability of device.
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021-2023 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
@ -16,27 +16,21 @@
|
||||
#include "enc_adv_data_prph.h"
|
||||
|
||||
#if CONFIG_EXAMPLE_ENC_ADV_DATA
|
||||
static uint8_t km_adv_pattern_1[] = {
|
||||
0x02, 0x01, 0x06,
|
||||
0x03, 0x03, 0x2C, 0x01,
|
||||
0x04, 0X09, 'k', 'e', 'y',
|
||||
};
|
||||
|
||||
static const char *tag = "ENC_ADV_DATA_PRPH";
|
||||
static int enc_adv_data_prph_gap_event(struct ble_gap_event *event, void *arg);
|
||||
const uint8_t device_name[3] = {'k', 'e', 'y'};
|
||||
|
||||
static uint8_t ext_adv_pattern_1[] = {
|
||||
0x02, 0x01, 0x06,
|
||||
0x03, 0x03, 0x2C, 0x00,
|
||||
0x05, 0X09, 'p', 'r', 'p', 'h',
|
||||
static uint8_t unencrypted_adv_pattern[] = {
|
||||
0x05, 0X09, 'p', 'r', 'p', 'h'
|
||||
};
|
||||
|
||||
struct key_material km = {
|
||||
.session_key = {
|
||||
0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB,
|
||||
0xCC, 0xCD, 0xCE, 0xCF
|
||||
0x19, 0x6a, 0x0a, 0xd1, 0x2a, 0x61, 0x20, 0x1e,
|
||||
0x13, 0x6e, 0x2e, 0xd1, 0x12, 0xda, 0xa9, 0x57
|
||||
},
|
||||
.iv = {0xFB, 0x56, 0xE1, 0xDA, 0xDC, 0x7E, 0xAD, 0xF5},
|
||||
.iv = {0x9E, 0x7a, 0x00, 0xef, 0xb1, 0x7a, 0xe7, 0x46},
|
||||
};
|
||||
|
||||
#if CONFIG_EXAMPLE_RANDOM_ADDR
|
||||
@ -74,31 +68,24 @@ enc_adv_data_prph_print_conn_desc(struct ble_gap_conn_desc *desc)
|
||||
desc->sec_state.bonded);
|
||||
}
|
||||
|
||||
static const struct enc_adv_data ead[] = {
|
||||
ENC_ADV_DATA(BLE_GAP_ENC_ADV_DATA, ext_adv_pattern_1, sizeof(ext_adv_pattern_1)),
|
||||
};
|
||||
|
||||
static void enc_adv_data_prph_encrypt_set(uint8_t instance, struct os_mbuf *data)
|
||||
static void
|
||||
enc_adv_data_prph_encrypt_set(uint8_t * out_encrypted_adv_data,
|
||||
const unsigned encrypted_adv_data_len)
|
||||
{
|
||||
int rc;
|
||||
|
||||
uint8_t enc_data_flag = BLE_GAP_ENC_ADV_DATA; //0x31
|
||||
const unsigned unencrypted_adv_data_len = sizeof(unencrypted_adv_pattern);
|
||||
|
||||
uint8_t ext_adv_pattern_sz = ead[0].len;
|
||||
uint8_t unencrypted_adv_data[unencrypted_adv_data_len];
|
||||
uint8_t encrypted_adv_data[encrypted_adv_data_len];
|
||||
|
||||
size_t adv_data_sz = BLE_GAP_DATA_SERIALIZED_SIZE(ext_adv_pattern_sz);
|
||||
uint8_t adv_data[adv_data_sz];
|
||||
memcpy(unencrypted_adv_data, unencrypted_adv_pattern, sizeof(unencrypted_adv_pattern));
|
||||
|
||||
size_t enc_adv_data_sz = BLE_EAD_ENCRYPTED_PAYLOAD_SIZE(adv_data_sz);
|
||||
uint8_t enc_adv_data[enc_adv_data_sz];
|
||||
MODLOG_DFLT(INFO, "Data before encryption:");
|
||||
print_bytes(unencrypted_adv_data, unencrypted_adv_data_len);
|
||||
MODLOG_DFLT(INFO, "\n");
|
||||
|
||||
ble_ead_serialize_data(&ead[0], adv_data);
|
||||
|
||||
MODLOG_DFLT(DEBUG, "Data before encryption:");
|
||||
print_bytes(adv_data, adv_data_sz);
|
||||
MODLOG_DFLT(DEBUG, "\n");
|
||||
|
||||
rc = ble_ead_encrypt(km.session_key, km.iv, adv_data, adv_data_sz, enc_adv_data);
|
||||
rc = ble_ead_encrypt(km.session_key, km.iv, unencrypted_adv_data, unencrypted_adv_data_len, encrypted_adv_data);
|
||||
if (rc == 0) {
|
||||
MODLOG_DFLT(INFO, "Encryption of adv data done successfully");
|
||||
} else {
|
||||
@ -106,20 +93,12 @@ static void enc_adv_data_prph_encrypt_set(uint8_t instance, struct os_mbuf *data
|
||||
return;
|
||||
}
|
||||
|
||||
MODLOG_DFLT(DEBUG, "Data after encryption:");
|
||||
print_bytes(enc_adv_data, enc_adv_data_sz);
|
||||
MODLOG_DFLT(DEBUG, "\n");
|
||||
MODLOG_DFLT(INFO, "Data after encryption:");
|
||||
print_bytes(encrypted_adv_data, encrypted_adv_data_len);
|
||||
MODLOG_DFLT(INFO, "\n");
|
||||
|
||||
//Copying encrypted data
|
||||
rc = os_mbuf_append(data, &enc_adv_data_sz, sizeof(uint8_t));
|
||||
|
||||
rc = os_mbuf_append(data, &enc_data_flag, sizeof(uint8_t));
|
||||
|
||||
rc = os_mbuf_append(data, enc_adv_data, enc_adv_data_sz);
|
||||
assert(rc == 0);
|
||||
|
||||
MODLOG_DFLT(INFO, "Advertising data:");
|
||||
print_mbuf(data);
|
||||
/** Contains Randomiser ## Encrypted Advertising Data ## MIC */
|
||||
memcpy(out_encrypted_adv_data, encrypted_adv_data, encrypted_adv_data_len);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -128,57 +107,59 @@ static void enc_adv_data_prph_encrypt_set(uint8_t instance, struct os_mbuf *data
|
||||
* o Undirected connectable mode.
|
||||
*/
|
||||
static void
|
||||
ext_enc_adv_data_prph_advertise(void)
|
||||
enc_adv_data_prph_advertise(void)
|
||||
{
|
||||
struct ble_gap_ext_adv_params params;
|
||||
uint8_t instance = 0;
|
||||
struct ble_gap_adv_params params;
|
||||
struct ble_hs_adv_fields fields;
|
||||
uint8_t own_addr_type;
|
||||
int rc;
|
||||
|
||||
struct os_mbuf *data;
|
||||
const unsigned encrypted_adv_data_len = BLE_EAD_ENCRYPTED_PAYLOAD_SIZE(sizeof(unencrypted_adv_pattern));
|
||||
uint8_t encrypted_adv_data[encrypted_adv_data_len];
|
||||
memset(encrypted_adv_data, 0, encrypted_adv_data_len);
|
||||
|
||||
/* First check if any instance is already active */
|
||||
if (ble_gap_ext_adv_active(instance)) {
|
||||
if (ble_gap_adv_active()) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* use defaults for non-set params */
|
||||
memset (¶ms, 0, sizeof(params));
|
||||
memset (&fields, 0, sizeof(fields));
|
||||
|
||||
own_addr_type = BLE_OWN_ADDR_PUBLIC;
|
||||
|
||||
/* enable connectable advertising */
|
||||
params.connectable = 1;
|
||||
|
||||
/* advertise using random addr */
|
||||
params.own_addr_type = BLE_OWN_ADDR_PUBLIC;
|
||||
|
||||
params.primary_phy = BLE_HCI_LE_PHY_1M;
|
||||
params.secondary_phy = BLE_HCI_LE_PHY_2M;
|
||||
//params.tx_power = 127;
|
||||
params.sid = 1;
|
||||
params.conn_mode = BLE_GAP_CONN_MODE_UND;
|
||||
params.disc_mode = BLE_GAP_DISC_MODE_GEN;
|
||||
|
||||
params.itvl_min = BLE_GAP_ADV_FAST_INTERVAL1_MIN;
|
||||
params.itvl_max = BLE_GAP_ADV_FAST_INTERVAL1_MIN;
|
||||
|
||||
/* configure instance 0 */
|
||||
rc = ble_gap_ext_adv_configure(instance, ¶ms, NULL,
|
||||
enc_adv_data_prph_gap_event, NULL);
|
||||
assert (rc == 0);
|
||||
fields.flags = BLE_HS_ADV_F_DISC_GEN | BLE_HS_ADV_F_BREDR_UNSUP;
|
||||
|
||||
/* in this case only scan response is allowed */
|
||||
/* get mbuf for scan rsp data */
|
||||
data = os_msys_get_pkthdr(sizeof(km_adv_pattern_1), 0);
|
||||
assert(data);
|
||||
fields.name = device_name;
|
||||
fields.name_len = 3;
|
||||
fields.name_is_complete = 1;
|
||||
|
||||
rc = os_mbuf_append(data, km_adv_pattern_1, sizeof(km_adv_pattern_1));
|
||||
assert(rc == 0);
|
||||
fields.uuids16 = (ble_uuid16_t[]) {
|
||||
BLE_UUID16_INIT(0x2C01) /** For the central to recognise this device */
|
||||
};
|
||||
fields.num_uuids16 = 1;
|
||||
fields.uuids16_is_complete = 1;
|
||||
|
||||
//Encrypted advertising data
|
||||
enc_adv_data_prph_encrypt_set(instance, data);
|
||||
/** Getting the encrypted advertising data */
|
||||
enc_adv_data_prph_encrypt_set(encrypted_adv_data, encrypted_adv_data_len);
|
||||
|
||||
rc = ble_gap_ext_adv_set_data(instance, data);
|
||||
fields.enc_adv_data = encrypted_adv_data;
|
||||
fields.enc_adv_data_len = encrypted_adv_data_len;
|
||||
|
||||
rc = ble_gap_adv_set_fields(&fields);
|
||||
assert (rc == 0);
|
||||
|
||||
/* start advertising */
|
||||
rc = ble_gap_ext_adv_start(instance, 0, 0);
|
||||
rc = ble_gap_adv_start(own_addr_type, NULL, BLE_HS_FOREVER,
|
||||
¶ms, enc_adv_data_prph_gap_event, NULL);
|
||||
assert (rc == 0);
|
||||
}
|
||||
|
||||
@ -218,7 +199,7 @@ enc_adv_data_prph_gap_event(struct ble_gap_event *event, void *arg)
|
||||
|
||||
if (event->connect.status != 0) {
|
||||
/* Connection failed; resume advertising. */
|
||||
ext_enc_adv_data_prph_advertise();
|
||||
enc_adv_data_prph_advertise();
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -229,7 +210,7 @@ enc_adv_data_prph_gap_event(struct ble_gap_event *event, void *arg)
|
||||
MODLOG_DFLT(INFO, "\n");
|
||||
|
||||
/* Connection terminated; resume advertising. */
|
||||
ext_enc_adv_data_prph_advertise();
|
||||
enc_adv_data_prph_advertise();
|
||||
return 0;
|
||||
|
||||
case BLE_GAP_EVENT_CONN_UPDATE:
|
||||
@ -247,6 +228,30 @@ enc_adv_data_prph_gap_event(struct ble_gap_event *event, void *arg)
|
||||
event->adv_complete.reason);
|
||||
return 0;
|
||||
|
||||
case BLE_GAP_EVENT_ENC_CHANGE:
|
||||
MODLOG_DFLT(INFO, "encryption change event; status=%d ",
|
||||
event->enc_change.status);
|
||||
rc = ble_gap_conn_find(event->enc_change.conn_handle, &desc);
|
||||
assert(rc == 0);
|
||||
enc_adv_data_prph_print_conn_desc(&desc);
|
||||
MODLOG_DFLT(INFO, "\n");
|
||||
return 0;
|
||||
|
||||
case BLE_GAP_EVENT_PASSKEY_ACTION:
|
||||
ESP_LOGI(tag, "PASSKEY_ACTION_EVENT started");
|
||||
struct ble_sm_io pkey = {0};
|
||||
|
||||
/** For now only BLE_SM_IOACT_DISP is handled */
|
||||
if (event->passkey.params.action == BLE_SM_IOACT_DISP) {
|
||||
pkey.action = event->passkey.params.action;
|
||||
pkey.passkey = 123456;
|
||||
ESP_LOGI(tag, "Enter passkey %" PRIu32 " on the peer side", pkey.passkey);
|
||||
rc = ble_sm_inject_io(event->passkey.conn_handle, &pkey);
|
||||
ESP_LOGI(tag, "ble_sm_inject_io result: %d", rc);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
case BLE_GAP_EVENT_NOTIFY_TX:
|
||||
MODLOG_DFLT(INFO, "notify_tx event; conn_handle=%d attr_handle=%d "
|
||||
"status=%d is_indication=%d",
|
||||
@ -274,6 +279,15 @@ enc_adv_data_prph_gap_event(struct ble_gap_event *event, void *arg)
|
||||
event->mtu.channel_id,
|
||||
event->mtu.value);
|
||||
return 0;
|
||||
|
||||
case BLE_GAP_EVENT_AUTHORIZE:
|
||||
MODLOG_DFLT(INFO, "authorization event; conn_handle=%d attr_handle=%d is_read=%d",
|
||||
event->authorize.conn_handle,
|
||||
event->authorize.attr_handle,
|
||||
event->authorize.is_read);
|
||||
/** Accept all authorization requests for now */
|
||||
event->authorize.out_response = BLE_GAP_AUTHORIZE_ACCEPT;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -337,7 +351,7 @@ enc_adv_data_prph_on_sync(void)
|
||||
MODLOG_DFLT(INFO, "\n");
|
||||
|
||||
/* Begin advertising. */
|
||||
ext_enc_adv_data_prph_advertise();
|
||||
enc_adv_data_prph_advertise();
|
||||
}
|
||||
|
||||
void enc_adv_data_prph_host_task(void *param)
|
||||
@ -373,17 +387,21 @@ app_main(void)
|
||||
ble_hs_cfg.gatts_register_cb = gatt_svr_register_cb;
|
||||
ble_hs_cfg.store_status_cb = ble_store_util_status_rr;
|
||||
|
||||
ble_hs_cfg.sm_io_cap = CONFIG_EXAMPLE_IO_TYPE;
|
||||
#ifdef CONFIG_EXAMPLE_BONDING
|
||||
#if CONFIG_EXAMPLE_BONDING
|
||||
ble_hs_cfg.sm_bonding = 1;
|
||||
/* Enable the appropriate bit masks to make sure the keys
|
||||
* that are needed are exchanged
|
||||
*/
|
||||
ble_hs_cfg.sm_our_key_dist |= BLE_SM_PAIR_KEY_DIST_ENC;
|
||||
ble_hs_cfg.sm_their_key_dist |= BLE_SM_PAIR_KEY_DIST_ENC;
|
||||
#else
|
||||
ble_hs_cfg.sm_bonding = 0;
|
||||
#endif
|
||||
|
||||
/** This feature requires authentication */
|
||||
ble_hs_cfg.sm_mitm = 1;
|
||||
ble_hs_cfg.sm_io_cap = CONFIG_EXAMPLE_IO_TYPE;
|
||||
|
||||
#ifdef CONFIG_EXAMPLE_USE_SC
|
||||
ble_hs_cfg.sm_sc = 1;
|
||||
#else
|
||||
@ -403,6 +421,7 @@ app_main(void)
|
||||
assert(rc == 0);
|
||||
|
||||
/* Set the session key and initialization vector */
|
||||
|
||||
rc = ble_svc_gap_device_key_material_set(km.session_key, km.iv);
|
||||
assert(rc == 0);
|
||||
|
||||
|
@ -11,4 +11,4 @@ CONFIG_BTDM_CTRL_MODE_BTDM=n
|
||||
CONFIG_BT_BLUEDROID_ENABLED=n
|
||||
CONFIG_BT_NIMBLE_ENABLED=y
|
||||
CONFIG_BT_NIMBLE_HCI_EVT_BUF_SIZE=70
|
||||
CONFIG_BT_NIMBLE_EXT_ADV=y
|
||||
CONFIG_BT_NIMBLE_EXT_ADV=n
|
||||
|
@ -0,0 +1,6 @@
|
||||
# The following lines of boilerplate have to be in your project's
|
||||
# CMakeLists in this exact order for cmake to work correctly
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(ble_pawr_adv)
|
@ -0,0 +1,75 @@
|
||||
| Supported Targets | ESP32-C6 |
|
||||
| ----------------- | -------- |
|
||||
|
||||
# BLE Periodic Advertiser Example
|
||||
|
||||
(See the README.md file in the upper level 'examples' directory for more information about examples.)
|
||||
|
||||
This example starts periodic advertising with non resolvable private address.
|
||||
|
||||
It uses Bluetooth controller and NimBLE stack based BLE host.
|
||||
|
||||
This example aims at understanding periodic advertisement and related NimBLE APIs.
|
||||
|
||||
|
||||
To test this demo, any BLE Periodic Sync app can be used.
|
||||
|
||||
|
||||
Note :
|
||||
|
||||
* Make sure to run `python -m pip install --user -r $IDF_PATH/requirements.txt -r $IDF_PATH/tools/ble/requirements.txt` to install the dependency packages needed.
|
||||
* Currently this Python utility is only supported on Linux (BLE communication is via BLuez + DBus).
|
||||
|
||||
## How to Use Example
|
||||
|
||||
Before project configuration and build, be sure to set the correct chip target using:
|
||||
|
||||
```bash
|
||||
idf.py set-target <chip_name>
|
||||
```
|
||||
|
||||
### Configure the project
|
||||
|
||||
Open the project configuration menu:
|
||||
|
||||
```bash
|
||||
idf.py menuconfig
|
||||
```
|
||||
|
||||
In the `Example Configuration` menu:
|
||||
|
||||
* Select I/O capabilities of device from `Example Configuration --> I/O Capability`, default is `Just_works`.
|
||||
|
||||
### Build and Flash
|
||||
|
||||
Run `idf.py -p PORT flash monitor` to build, flash and monitor the project.
|
||||
|
||||
(To exit the serial monitor, type ``Ctrl-]``.)
|
||||
|
||||
See the [Getting Started Guide](https://idf.espressif.com/) for full steps to configure and use ESP-IDF to build projects.
|
||||
|
||||
## Example Output
|
||||
|
||||
There is this console output when periodic_adv is started:
|
||||
|
||||
```
|
||||
I (313) BTDM_INIT: BT controller compile version [2ee0168]
|
||||
I (313) phy_init: phy_version 912,d001756,Jun 2 2022,16:28:07
|
||||
I (353) system_api: Base MAC address is not set
|
||||
I (353) system_api: read default base MAC address from EFUSE
|
||||
I (353) BTDM_INIT: Bluetooth MAC: 84:f7:03:08:14:8e
|
||||
|
||||
I (363) NimBLE_BLE_PERIODIC_ADV: BLE Host Task Started
|
||||
I (373) NimBLE: Device Address:
|
||||
I (373) NimBLE: d0:42:3a:95:84:05
|
||||
I (373) NimBLE:
|
||||
|
||||
I (383) NimBLE: instance 1 started (periodic)
|
||||
```
|
||||
|
||||
## Note
|
||||
* Periodic sync transfer is not implemented for now.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
For any technical queries, please open an [issue](https://github.com/espressif/esp-idf/issues) on GitHub. We will get back to you soon.
|
@ -0,0 +1,4 @@
|
||||
set(srcs "main.c")
|
||||
|
||||
idf_component_register(SRCS "${srcs}"
|
||||
INCLUDE_DIRS ".")
|
@ -0,0 +1,27 @@
|
||||
menu "Example Configuration"
|
||||
|
||||
config EXAMPLE_EXTENDED_ADV
|
||||
bool
|
||||
depends on SOC_BLE_50_SUPPORTED
|
||||
default y if SOC_ESP_NIMBLE_CONTROLLER
|
||||
select BT_NIMBLE_EXT_ADV
|
||||
prompt "Enable Extended Adv"
|
||||
help
|
||||
Use this option to enable extended advertising in the example.
|
||||
If you disable this option, ensure config BT_NIMBLE_EXT_ADV is
|
||||
also disabled from Nimble stack menuconfig.
|
||||
|
||||
config EXAMPLE_RANDOM_ADDR
|
||||
bool
|
||||
prompt "Advertise RANDOM Address"
|
||||
help
|
||||
Use this option to advertise a random address instead of public address
|
||||
|
||||
config EXAMPLE_PERIODIC_ADV_ENH
|
||||
bool
|
||||
prompt "Enable Periodic Adv Enhancements"
|
||||
depends on SOC_BLE_50_SUPPORTED && SOC_BLE_PERIODIC_ADV_ENH_SUPPORTED
|
||||
select BT_NIMBLE_PERIODIC_ADV_ENH
|
||||
help
|
||||
Use this option to enable periodic advertising enhancements
|
||||
endmenu
|
@ -0,0 +1,3 @@
|
||||
dependencies:
|
||||
nimble_peripheral_utils:
|
||||
path: ${IDF_PATH}/examples/bluetooth/nimble/common/nimble_peripheral_utils
|
220
examples/bluetooth/nimble/ble_pawr_adv/ble_pawr_adv/main/main.c
Normal file
220
examples/bluetooth/nimble/ble_pawr_adv/ble_pawr_adv/main/main.c
Normal file
@ -0,0 +1,220 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
#include "esp_log.h"
|
||||
#include "nvs_flash.h"
|
||||
/* BLE */
|
||||
#include "nimble/nimble_port.h"
|
||||
#include "nimble/nimble_port_freertos.h"
|
||||
#include "host/ble_hs.h"
|
||||
|
||||
#define BLE_PAWR_NUM_SUBEVTS (5)
|
||||
#define BLE_PAWR_SUB_INTERVAL (12) /*!< Interval between subevents (N * 1.25 ms) */
|
||||
#define BLE_PAWR_RSP_SLOT_DELAY (1) /*!< The first response slot delay (N * 1.25 ms)*/
|
||||
#define BLE_PAWR_RSP_SLOT_SPACING (32) /*!< Time between response slots (N * 0.125 ms) */
|
||||
#define BLE_PAWR_NUM_RSP_SLOTS (3) /*!< Number of subevent response slots */
|
||||
#define BLE_PAWR_SUB_DATA_LEN (10)
|
||||
|
||||
#define TAG "NimBLE_BLE_PAwR"
|
||||
|
||||
static struct ble_gap_set_periodic_adv_subev_data_params sub_data_params[BLE_PAWR_NUM_SUBEVTS];
|
||||
static uint8_t sub_data_pattern[BLE_PAWR_SUB_DATA_LEN] = {0};
|
||||
|
||||
static int
|
||||
gap_event_cb(struct ble_gap_event *event, void *arg)
|
||||
{
|
||||
int rc;
|
||||
uint8_t sub;
|
||||
uint8_t sent_num;
|
||||
struct os_mbuf *data;
|
||||
|
||||
switch (event->type) {
|
||||
|
||||
case BLE_GAP_EVENT_PER_SUBEV_DATA_REQ:
|
||||
ESP_LOGI(TAG, "[Request] data: %x, subevt start:%d, subevt count:%d",
|
||||
sub_data_pattern[0],
|
||||
event->periodic_adv_subev_data_req.subevent_start,
|
||||
event->periodic_adv_subev_data_req.subevent_data_count);
|
||||
|
||||
sent_num = event->periodic_adv_subev_data_req.subevent_data_count;
|
||||
for (uint8_t i = 0; i < sent_num; i++) {
|
||||
data = os_msys_get_pkthdr(BLE_PAWR_SUB_DATA_LEN, 0);
|
||||
if (!data) {
|
||||
ESP_LOGE(TAG, "No memory, %d", i);
|
||||
break;
|
||||
}
|
||||
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);
|
||||
os_mbuf_append(data, sub_data_pattern, BLE_PAWR_SUB_DATA_LEN);
|
||||
sub_data_params[i].subevent = sub;
|
||||
sub_data_params[i].response_slot_start = 0;
|
||||
sub_data_params[i].response_slot_count = BLE_PAWR_NUM_RSP_SLOTS;
|
||||
sub_data_params[i].data = data;
|
||||
sub_data_pattern[0]++;
|
||||
}
|
||||
|
||||
rc = ble_gap_set_periodic_adv_subev_data(event->periodic_adv_subev_data_req.adv_handle,
|
||||
sent_num, sub_data_params);
|
||||
if (rc) {
|
||||
ESP_LOGE(TAG, "Failed to set Subevent Data, rc = 0x%x", rc);
|
||||
}
|
||||
return 0;
|
||||
|
||||
case BLE_GAP_EVENT_PER_SUBEV_RESP:
|
||||
|
||||
if (event->periodic_adv_response.data_status == BLE_GAP_PER_ADV_DATA_STATUS_INCOMPLETE) {
|
||||
// ESP_LOGI(TAG,"Incomplete response report received, discarding \n");
|
||||
}
|
||||
else if (event->periodic_adv_response.data_status == BLE_GAP_PER_ADV_DATA_STATUS_RX_FAILED) {
|
||||
// ESP_LOGI(TAG,"Controller failed to received the AUX_SYNC_SUBEVENT_RSP\n");
|
||||
}
|
||||
else if (event->periodic_adv_response.data_status == BLE_GAP_PER_ADV_DATA_STATUS_COMPLETE) {
|
||||
ESP_LOGI(TAG, "[Response] subevent:%d, response_slot:%d, data_length:%d, data:%x",
|
||||
event->periodic_adv_response.subevent,
|
||||
event->periodic_adv_response.response_slot,
|
||||
event->periodic_adv_response.data_length,
|
||||
event->periodic_adv_response.data[0]);
|
||||
}
|
||||
else if (event->periodic_adv_response.data_status == BLE_GAP_PER_ADV_DATA_STATUS_TRUNCATED) {
|
||||
// ESP_LOGI(TAG,"Truncated response report received, discarding\n");
|
||||
}
|
||||
else {
|
||||
ESP_LOGE(TAG,"Invalid data status\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
start_periodic_adv(void)
|
||||
{
|
||||
int rc;
|
||||
uint8_t addr[6];
|
||||
struct ble_gap_periodic_adv_params pparams;
|
||||
struct ble_gap_ext_adv_params params;
|
||||
struct ble_hs_adv_fields adv_fields;
|
||||
struct os_mbuf *data;
|
||||
uint8_t instance = 0;
|
||||
|
||||
#if MYNEWT_VAL(BLE_PERIODIC_ADV_ENH)
|
||||
struct ble_gap_periodic_adv_enable_params eparams;
|
||||
memset(&eparams, 0, sizeof(eparams));
|
||||
#endif
|
||||
|
||||
/* Get the local public address. */
|
||||
rc = ble_hs_id_copy_addr(BLE_ADDR_PUBLIC, addr, NULL);
|
||||
assert (rc == 0);
|
||||
|
||||
ESP_LOGI(TAG, "Device Address %02x:%02x:%02x:%02x:%02x:%02x", addr[5], addr[4], addr[3],
|
||||
addr[2], addr[1], addr[0]);
|
||||
|
||||
/* For periodic we use instance with non-connectable advertising */
|
||||
memset (¶ms, 0, sizeof(params));
|
||||
params.own_addr_type = BLE_OWN_ADDR_PUBLIC;
|
||||
params.primary_phy = BLE_HCI_LE_PHY_CODED;
|
||||
params.secondary_phy = BLE_HCI_LE_PHY_1M;
|
||||
params.sid = 0;
|
||||
params.itvl_min = BLE_GAP_ADV_ITVL_MS(100);
|
||||
params.itvl_max = BLE_GAP_ADV_ITVL_MS(100);
|
||||
|
||||
rc = ble_gap_ext_adv_configure(instance, ¶ms, NULL, gap_event_cb, NULL);
|
||||
assert (rc == 0);
|
||||
|
||||
memset(&adv_fields, 0, sizeof(adv_fields));
|
||||
adv_fields.name = (const uint8_t *)"Nimble_PAwR";
|
||||
adv_fields.name_len = strlen((char *)adv_fields.name);
|
||||
|
||||
/* mbuf chain will be increased if needed */
|
||||
data = os_msys_get_pkthdr(BLE_HCI_MAX_ADV_DATA_LEN, 0);
|
||||
assert(data);
|
||||
|
||||
rc = ble_hs_adv_set_fields_mbuf(&adv_fields, data);
|
||||
assert(rc == 0);
|
||||
|
||||
rc = ble_gap_ext_adv_set_data(instance, data);
|
||||
assert(rc == 0);
|
||||
|
||||
/* configure periodic advertising */
|
||||
memset(&pparams, 0, sizeof(pparams));
|
||||
pparams.include_tx_power = 0;
|
||||
pparams.itvl_min = BLE_GAP_PERIODIC_ITVL_MS(3000);
|
||||
pparams.itvl_max = BLE_GAP_PERIODIC_ITVL_MS(3000);
|
||||
/* Configure the parameters of PAwR. */
|
||||
pparams.num_subevents = BLE_PAWR_NUM_SUBEVTS;
|
||||
pparams.subevent_interval = BLE_GAP_PERIODIC_ITVL_MS(300);
|
||||
pparams.response_slot_delay = BLE_GAP_PERIODIC_ITVL_MS(80);
|
||||
pparams.response_slot_spacing = 0xFF;
|
||||
pparams.num_response_slots = BLE_PAWR_NUM_RSP_SLOTS;
|
||||
|
||||
rc = ble_gap_periodic_adv_configure(instance, &pparams);
|
||||
assert(rc == 0);
|
||||
|
||||
/* start periodic advertising */
|
||||
#if MYNEWT_VAL(BLE_PERIODIC_ADV_ENH)
|
||||
eparams.include_adi = 1;
|
||||
rc = ble_gap_periodic_adv_start(instance, &eparams);
|
||||
#else
|
||||
rc = ble_gap_periodic_adv_start(instance);
|
||||
#endif
|
||||
assert (rc == 0);
|
||||
|
||||
/* start advertising */
|
||||
rc = ble_gap_ext_adv_start(instance, 0, 0);
|
||||
assert (rc == 0);
|
||||
|
||||
ESP_LOGI(TAG, "instance %u started (periodic)\n", instance);
|
||||
}
|
||||
|
||||
static void
|
||||
on_reset(int reason)
|
||||
{
|
||||
ESP_LOGE(TAG, "Resetting state; reason=%d\n", reason);
|
||||
}
|
||||
|
||||
static void
|
||||
on_sync(void)
|
||||
{
|
||||
/* Begin advertising. */
|
||||
start_periodic_adv();
|
||||
}
|
||||
|
||||
void pawr_host_task(void *param)
|
||||
{
|
||||
ESP_LOGI(TAG, "BLE Host Task Started");
|
||||
/* This function will return only when nimble_port_stop() is executed */
|
||||
nimble_port_run();
|
||||
|
||||
nimble_port_freertos_deinit();
|
||||
}
|
||||
|
||||
void
|
||||
app_main(void)
|
||||
{
|
||||
esp_err_t ret;
|
||||
|
||||
/* Initialize NVS — it is used to store PHY calibration data */
|
||||
ret = nvs_flash_init();
|
||||
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
|
||||
ESP_ERROR_CHECK(nvs_flash_erase());
|
||||
ret = nvs_flash_init();
|
||||
}
|
||||
ESP_ERROR_CHECK(ret);
|
||||
|
||||
ret = nimble_port_init();
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to init nimble %d ", ret);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Initialize the NimBLE host configuration. */
|
||||
ble_hs_cfg.reset_cb = on_reset;
|
||||
ble_hs_cfg.sync_cb = on_sync;
|
||||
|
||||
nimble_port_freertos_init(pawr_host_task);
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
# Override some defaults so BT stack is enabled
|
||||
# in this example
|
||||
|
||||
#
|
||||
# BT config
|
||||
#
|
||||
CONFIG_BT_ENABLED=y
|
||||
CONFIG_BT_BLUEDROID_ENABLED=n
|
||||
CONFIG_BT_NIMBLE_ENABLED=y
|
||||
CONFIG_BT_NIMBLE_EXT_ADV=y
|
||||
CONFIG_BT_NIMBLE_PERIODIC_ADV_WITH_RESPONSES=y
|
||||
CONFIG_BT_NIMBLE_MAX_PERIODIC_SYNCS=1
|
||||
CONFIG_BT_NIMBLE_ROLE_CENTRAL=y
|
||||
CONFIG_BT_NIMBLE_ROLE_OBSERVER=n
|
||||
|
||||
CONFIG_BT_CONTROLLER_DISABLED=y
|
||||
|
||||
#
|
||||
# Host-controller Transport
|
||||
#
|
||||
CONFIG_BT_NIMBLE_TRANSPORT_UART_PORT=1
|
||||
CONFIG_UART_BAUDRATE_115200=y
|
||||
CONFIG_BT_NIMBLE_UART_TX_PIN=20
|
||||
CONFIG_BT_NIMBLE_UART_RX_PIN=21
|
||||
CONFIG_UART_HW_FLOWCTRL_CTS_RTS=n
|
||||
# CONFIG_BT_NIMBLE_HCI_UART_RTS_PIN=22
|
||||
# CONFIG_BT_NIMBLE_HCI_UART_CTS_PIN=23
|
||||
# end of Host-controller Transport
|
||||
# end of NimBLE Options
|
||||
|
||||
# C6 Nordic
|
||||
# TX: 20 ---- RX
|
||||
# RX: 21 ---- TX
|
||||
# RTS: 22 ---- CTS
|
||||
# CTS: 23 ---- RTS
|
@ -0,0 +1,320 @@
|
||||
# BLE Periodic Advertisement Example Walkthrough
|
||||
|
||||
## Introduction
|
||||
|
||||
In this tutorial, the ble_periodic_adv example code for the espressif chipsets with BLE5.0 support is reviewed. This example aims at understanding periodic advertisements and related NimBLE APIs.This code implements the periodic advertisement functionality along with extended advertisement by generating a non-resolvable private address.
|
||||
|
||||
## Includes
|
||||
|
||||
This example is located in the examples folder of the ESP-IDF under the [ble_periodic_adv/main](../main). The [main.c](../main/main.c) file located in the main folder contains all the functionality that we are going to review. The header files contained in [main.c](../main/main.c) are:
|
||||
|
||||
```c
|
||||
#include "esp_log.h"
|
||||
#include "nvs_flash.h"
|
||||
/* BLE */
|
||||
#include "nimble/nimble_port.h"
|
||||
#include "nimble/nimble_port_freertos.h"
|
||||
#include "host/ble_hs.h"
|
||||
#include "host/util/util.h"
|
||||
#include "console/console.h"
|
||||
#include "services/gap/ble_svc_gap.h"
|
||||
#include "periodic_adv.h"
|
||||
#include "host/ble_gap.h"
|
||||
#include "host/ble_hs_adv.h"
|
||||
#include "patterns.h"
|
||||
```
|
||||
These `includes` are required for the FreeRTOS and underlying system components to run, including the logging functionality and a library to store data in non-volatile flash memory. We are interested in `“nimble_port.h”`, `“nimble_port_freertos.h”`, `"ble_hs.h"` and `“ble_svc_gap.h”`, `“periodic_adv.h”` which expose the BLE APIs required to implement this example.
|
||||
|
||||
* `nimble_port.h`: Includes the declaration of functions required for the initialization of the nimble stack.
|
||||
* `nimble_port_freertos.h`: Initializes and enables nimble host task.
|
||||
* `ble_hs.h`: Defines the functionalities to handle the host event
|
||||
* `ble_svc_gap.h`:Defines the macros for device name, and device appearance and declares the function to set them.
|
||||
* `periodic_adv.h`:It includes the code containing forward declarations of 2 structs `ble_hs_cfg` , and `ble_gatt_register_ctxt` based on weather macro `H_BLE_PERIODIC_ADV_` is defined.
|
||||
|
||||
## Main Entry Point
|
||||
|
||||
The program’s entry point is the app_main() function:
|
||||
```c
|
||||
void
|
||||
app_main(void)
|
||||
{
|
||||
int rc;
|
||||
/* Initialize NVS — it is used to store PHY calibration data */
|
||||
esp_err_t ret = nvs_flash_init();
|
||||
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
|
||||
ESP_ERROR_CHECK(nvs_flash_erase());
|
||||
ret = nvs_flash_init();
|
||||
}
|
||||
ESP_ERROR_CHECK(ret);
|
||||
|
||||
ret = nimble_port_init();
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(tag, "Failed to init nimble %d ", ret);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Initialize the NimBLE host configuration. */
|
||||
ble_hs_cfg.reset_cb = periodic_adv_on_reset;
|
||||
ble_hs_cfg.sync_cb = periodic_adv_on_sync;
|
||||
ble_hs_cfg.store_status_cb = ble_store_util_status_rr;
|
||||
/* Set the default device name. */
|
||||
rc = ble_svc_gap_device_name_set("nimble_periodic_adv");
|
||||
assert(rc == 0);
|
||||
|
||||
/* XXX Need to have a template for store */
|
||||
ble_store_config_init();
|
||||
|
||||
nimble_port_freertos_init(periodic_adv_host_task);
|
||||
}
|
||||
```
|
||||
The main function starts by initializing the non-volatile storage library. This library allows us to save the key-value pairs in flash memory.`nvs_flash_init()` stores the PHY calibration data. In a Bluetooth Low Energy (BLE) device, cryptographic keys used for encryption and authentication are often stored in Non-Volatile Storage (NVS).BLE stores the peer keys, CCCD keys, peer records, etc on NVS. By storing these keys in NVS, the BLE device can quickly retrieve them when needed, without the need for time-consuming key generations.
|
||||
```c
|
||||
esp_err_t ret = nvs_flash_init();
|
||||
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
|
||||
ESP_ERROR_CHECK(nvs_flash_erase());
|
||||
ret = nvs_flash_init();
|
||||
}
|
||||
ESP_ERROR_CHECK( ret );
|
||||
```
|
||||
|
||||
## BT Controller and Stack Initialization
|
||||
|
||||
The main function calls `nimble_port_init()` to initialize BT Controller and nimble stack. This function initializes the BT controller by first creating its configuration structure named `esp_bt_controller_config_t` with default settings generated by the `BT_CONTROLLER_INIT_CONFIG_DEFAULT()` macro. It implements the Host Controller Interface (HCI) on the controller side, the Link Layer (LL), and the Physical Layer (PHY). The BT Controller is invisible to the user applications and deals with the lower layers of the BLE stack. The controller configuration includes setting the BT controller stack size, priority, and HCI baud rate. With the settings created, the BT controller is initialized and enabled with the `esp_bt_controller_init()` and `esp_bt_controller_enable()` functions:
|
||||
|
||||
```c
|
||||
esp_bt_controller_config_t config_opts = BT_CONTROLLER_INIT_CONFIG_DEFAULT();
|
||||
ret = esp_bt_controller_init(&config_opts);
|
||||
```
|
||||
Next, the controller is enabled in BLE Mode.
|
||||
|
||||
```c
|
||||
ret = esp_bt_controller_enable(ESP_BT_MODE_BLE);
|
||||
```
|
||||
The controller should be enabled in `ESP_BT_MODE_BLE` if you want to use the BLE mode.
|
||||
|
||||
There are four Bluetooth modes supported:
|
||||
|
||||
1. `ESP_BT_MODE_IDLE`: Bluetooth not running
|
||||
2. `ESP_BT_MODE_BLE`: BLE mode
|
||||
3. `ESP_BT_MODE_CLASSIC_BT`: BT Classic mode
|
||||
4. `ESP_BT_MODE_BTDM`: Dual mode (BLE + BT Classic)
|
||||
|
||||
After the initialization of the BT controller, the nimble stack, which includes the common definitions and APIs for BLE, is initialized by using `esp_nimble_init()`:
|
||||
|
||||
```c
|
||||
esp_err_t esp_nimble_init(void)
|
||||
{
|
||||
|
||||
#if !SOC_ESP_NIMBLE_CONTROLLER
|
||||
/* Initialize the function pointers for OS porting */
|
||||
npl_freertos_funcs_init();
|
||||
|
||||
npl_freertos_mempool_init();
|
||||
|
||||
if(esp_nimble_hci_init() != ESP_OK) {
|
||||
ESP_LOGE(NIMBLE_PORT_LOG_TAG, "hci inits failed\n");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
/* Initialize default event queue */
|
||||
ble_npl_eventq_init(&g_eventq_dflt);
|
||||
|
||||
os_msys_init();
|
||||
|
||||
void ble_store_ram_init(void);
|
||||
/* XXX Need to have a template for store */
|
||||
ble_store_ram_init();
|
||||
#endif
|
||||
|
||||
/* Initialize the host */
|
||||
ble_hs_init();
|
||||
return ESP_OK;
|
||||
}
|
||||
```
|
||||
|
||||
The host is configured by setting up the callbacks for Stack-reset, Stack-sync, and Storage status
|
||||
|
||||
```c
|
||||
ble_hs_cfg.reset_cb = periodic_adv_on_reset;
|
||||
ble_hs_cfg.sync_cb = periodic_adv_on_sync;
|
||||
ble_hs_cfg.store_status_cb = ble_store_util_status_rr;
|
||||
```
|
||||
|
||||
The main function calls `ble_svc_gap_device_name_set()` to set the default device name. 'blecent_phy' is passed as the default device name to this function.
|
||||
```c
|
||||
rc = ble_svc_gap_device_name_set("nimble_periodic_adv");
|
||||
```
|
||||
main function calls `ble_store_config_init()` to configure the host by setting up the storage callbacks which handle the read, write, and deletion of security material.
|
||||
```c
|
||||
/* XXX Need to have a template for store */
|
||||
ble_store_config_init();
|
||||
```
|
||||
|
||||
The main function ends by creating a task where nimble will run using `nimble_port_freertos_init()`. This enables the nimble stack by using `esp_nimble_enable()`.
|
||||
|
||||
```c
|
||||
nimble_port_freertos_init(periodic_adv_host_task);
|
||||
```
|
||||
`esp_nimble_enable()` create a task where the nimble host will run. It is not strictly necessary to have a separate task for the nimble host, but to handle the default queue, it is easier to create a separate task.
|
||||
|
||||
## Generation of non-resolvable private address
|
||||
|
||||
In Bluetooth Low Energy (BLE), a non-resolvable private address is a type of Bluetooth device address that is used for privacy purposes. It is a randomly generated address that changes periodically to prevent long-term tracking of a device. The API call to `ble_hs_id_gen_rnd()` is responsible for generating a non-resolvable private address. NRPA is a 48-bit address that is stored in `addr.val`.
|
||||
|
||||
```c
|
||||
#if CONFIG_EXAMPLE_RANDOM_ADDR
|
||||
static void
|
||||
periodic_adv_set_addr(void)
|
||||
{
|
||||
ble_addr_t addr;
|
||||
int rc;
|
||||
|
||||
/* generate new non-resolvable private address */
|
||||
rc = ble_hs_id_gen_rnd(0, &addr);
|
||||
assert(rc == 0);
|
||||
|
||||
/* set generated address */
|
||||
rc = ble_hs_id_set_rnd(addr.val);
|
||||
|
||||
assert(rc == 0);
|
||||
}
|
||||
#endif
|
||||
```
|
||||
|
||||
## Periodic Advertisement
|
||||
|
||||
Periodic advertisement start by creating instances of structures `ble_gap_periodic_adv_params`, `ble_gap_ext_adv_params`, `ble_hs_adv_fields`, and `os_mbuf`. Advertising parameters such as connecting modes, advertising intervals, peer address, advertising-filter policy, etc are defined in these structures for periodic and extended advertisements. `pparams` and `params` instances have parameters for periodic advertisement and extended advertisement respectively.
|
||||
Bluetooth device address is given by the structure ble_aadr_t which contains the fields for address type and address value.
|
||||
|
||||
## Need of Extended Advertisement in Periodic Advertisement
|
||||
|
||||
Non-connectable and non-scannable advertising events containing synchronization information about a periodic advertising train are necessary for the scanner device to sync with the periodic advertising train. The periodic advertising will utilize the same physical layer (PHY) as the auxiliary packet, which is part of the extended advertisement.
|
||||
|
||||
|
||||
Below is the implementation to start periodic advertisement.
|
||||
|
||||
```c
|
||||
static void
|
||||
start_periodic_adv(void)
|
||||
{
|
||||
int rc;
|
||||
struct ble_gap_periodic_adv_params pparams;
|
||||
struct ble_gap_ext_adv_params params;
|
||||
struct ble_hs_adv_fields adv_fields;
|
||||
struct os_mbuf *data;
|
||||
uint8_t instance = 1;
|
||||
ble_addr_t addr;
|
||||
|
||||
/* set random (NRPA) address for instance */
|
||||
rc = ble_hs_id_gen_rnd(1, &addr);
|
||||
assert (rc == 0);
|
||||
|
||||
MODLOG_DFLT(INFO, "Device Address: ");
|
||||
print_addr(addr.val);
|
||||
MODLOG_DFLT(INFO, "\n");
|
||||
|
||||
/* For periodic we use instance with non-connectable advertising */
|
||||
memset (¶ms, 0, sizeof(params));
|
||||
|
||||
/* advertise using random addr */
|
||||
params.own_addr_type = BLE_OWN_ADDR_RANDOM;
|
||||
params.primary_phy = BLE_HCI_LE_PHY_1M;
|
||||
params.secondary_phy = BLE_HCI_LE_PHY_2M;
|
||||
params.sid = 2;
|
||||
|
||||
/* configure instance 1 */
|
||||
rc = ble_gap_ext_adv_configure(instance, ¶ms, NULL, NULL, NULL);
|
||||
assert (rc == 0);
|
||||
|
||||
rc = ble_gap_ext_adv_set_addr(instance, &addr );
|
||||
assert (rc == 0);
|
||||
|
||||
memset(&adv_fields, 0, sizeof(adv_fields));
|
||||
adv_fields.name = (const uint8_t *)"Periodic ADV";
|
||||
adv_fields.name_len = strlen((char *)adv_fields.name);
|
||||
|
||||
/* Default to legacy PDUs size, mbuf chain will be increased if needed
|
||||
*/
|
||||
data = os_msys_get_pkthdr(BLE_HCI_MAX_ADV_DATA_LEN, 0);
|
||||
assert(data);
|
||||
|
||||
rc = ble_hs_adv_set_fields_mbuf(&adv_fields, data);
|
||||
assert(rc == 0);
|
||||
|
||||
rc = ble_gap_ext_adv_set_data(instance, data);
|
||||
assert(rc == 0);
|
||||
|
||||
/* configure periodic advertising */
|
||||
memset(&pparams, 0, sizeof(pparams));
|
||||
pparams.include_tx_power = 0;
|
||||
pparams.itvl_min = BLE_GAP_ADV_ITVL_MS(120);
|
||||
pparams.itvl_max = BLE_GAP_ADV_ITVL_MS(240);
|
||||
|
||||
rc = ble_gap_periodic_adv_configure(instance, &pparams);
|
||||
assert(rc == 0);
|
||||
|
||||
data = os_msys_get_pkthdr(sizeof(periodic_adv_raw_data), 0);
|
||||
assert(data);
|
||||
|
||||
rc = os_mbuf_append(data, periodic_adv_raw_data, sizeof(periodic_adv_raw_data));
|
||||
assert(rc == 0);
|
||||
rc = ble_gap_periodic_adv_set_data(instance, data);
|
||||
assert (rc == 0);
|
||||
|
||||
/* start periodic advertising */
|
||||
assert (rc == 0 rc = ble_gap_periodic_adv_start(instance);
|
||||
);
|
||||
|
||||
/* start advertising */
|
||||
rc = ble_gap_ext_adv_start(instance, 0, 0);
|
||||
assert (rc == 0);
|
||||
|
||||
MODLOG_DFLT(INFO, "instance %u started (periodic)\n", instance);
|
||||
}
|
||||
```
|
||||
|
||||
The periodic advertisement uses a non-connectable advertising mode. `memset (¶ms, 0, sizeof(params))` initialises params to 0. This also sets `params.connectable` to 0.
|
||||
|
||||
## Parameter Configuration
|
||||
|
||||
The below snippets represent the parameter configuration for extended and periodic advertisement.
|
||||
|
||||
### For Extended Advertisement
|
||||
|
||||
```c
|
||||
params.own_addr_type = BLE_OWN_ADDR_RANDOM; //Own address type is set to Random
|
||||
params.primary_phy = BLE_HCI_LE_PHY_1M; // Primary advertising PHY is set to 1M
|
||||
params.secondary_phy = BLE_HCI_LE_PHY_2M; // Secondary advertising PHY is set to 2M
|
||||
params.sid = 2; // Advertising set Id is assigned with value 2.
|
||||
```
|
||||
|
||||
### For Periodic Advertisement
|
||||
|
||||
```c
|
||||
memset(&pparams, 0, sizeof(pparams));
|
||||
pparams.include_tx_power = 0; // Indicates that TX power is not included in advertising PDU
|
||||
pparams.itvl_min = BLE_GAP_ADV_ITVL_MS(120); // Minimum advertising interval of 240ms
|
||||
pparams.itvl_max = BLE_GAP_ADV_ITVL_MS(240); //Maximum advertising interval of 480ms
|
||||
```
|
||||
|
||||
Periodic advertisement is started for a particular advertisement instance by calling the API `ble_gap_periodic_adv_start(instance)`. This function takes instance-id as an input parameter. It defines the hci command by initializing the command parameters which are represented in the following lines.
|
||||
|
||||
```c
|
||||
struct ble_hci_le_set_periodic_adv_enable_cp cmd;
|
||||
cmd.enable = 0x01;
|
||||
cmd.adv_handle = instance;
|
||||
```
|
||||
|
||||
Extended advertising is invoked for a particular instance using the API call `ble_gap_ext_adv_start(instance, 0, 0)`.Instance-id, duration, and max_events are input parameters for this API call respectively.
|
||||
|
||||
Duration represents the time for which the adverteiment will take place. Upon expiration, the advertising procedure ends, and the BLE_GAP_EVENT_ADV_COMPLETE event is reported.0 value is used for no expiration.
|
||||
|
||||
max_events Number of advertising events that should be sent before advertising ends and a BLE_GAP_EVENT_ADV_COMPLETE event is reported.0 value is used for no limit.
|
||||
|
||||
## Conclusion
|
||||
|
||||
This Walkthrough covers the code explanation of the BLE_PERIODIC_ADV. The following points are concluded through this walkthrough.
|
||||
|
||||
1. Periodic advertising allows the scanner to sync with the advertiser so the scanner and advertiser would wake up at the same time.
|
||||
2. Periodic advertisement uses NRPA (Non Resolvable private address). It is a randomly generated address that changes periodically to prevent long-term tracking of a device.
|
||||
3. Extended advertising is used to indicate to the scanner that the advertiser is utilizing periodic advertising. Therefore, periodic advertising is started before extended advertising so that the scanner and advertiser can synchronize their actions and operate at the same time.
|
@ -0,0 +1,6 @@
|
||||
# The following lines of boilerplate have to be in your project's
|
||||
# CMakeLists in this exact order for cmake to work correctly
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(ble_pawr_sync)
|
@ -0,0 +1,75 @@
|
||||
| Supported Targets | ESP32-C6 |
|
||||
| ----------------- | -------- |
|
||||
|
||||
# BLE Periodic Advertiser Example
|
||||
|
||||
(See the README.md file in the upper level 'examples' directory for more information about examples.)
|
||||
|
||||
This example starts periodic advertising with non resolvable private address.
|
||||
|
||||
It uses Bluetooth controller and NimBLE stack based BLE host.
|
||||
|
||||
This example aims at understanding periodic advertisement and related NimBLE APIs.
|
||||
|
||||
|
||||
To test this demo, any BLE Periodic Sync app can be used.
|
||||
|
||||
|
||||
Note :
|
||||
|
||||
* Make sure to run `python -m pip install --user -r $IDF_PATH/requirements.txt -r $IDF_PATH/tools/ble/requirements.txt` to install the dependency packages needed.
|
||||
* Currently this Python utility is only supported on Linux (BLE communication is via BLuez + DBus).
|
||||
|
||||
## How to Use Example
|
||||
|
||||
Before project configuration and build, be sure to set the correct chip target using:
|
||||
|
||||
```bash
|
||||
idf.py set-target <chip_name>
|
||||
```
|
||||
|
||||
### Configure the project
|
||||
|
||||
Open the project configuration menu:
|
||||
|
||||
```bash
|
||||
idf.py menuconfig
|
||||
```
|
||||
|
||||
In the `Example Configuration` menu:
|
||||
|
||||
* Select I/O capabilities of device from `Example Configuration --> I/O Capability`, default is `Just_works`.
|
||||
|
||||
### Build and Flash
|
||||
|
||||
Run `idf.py -p PORT flash monitor` to build, flash and monitor the project.
|
||||
|
||||
(To exit the serial monitor, type ``Ctrl-]``.)
|
||||
|
||||
See the [Getting Started Guide](https://idf.espressif.com/) for full steps to configure and use ESP-IDF to build projects.
|
||||
|
||||
## Example Output
|
||||
|
||||
There is this console output when periodic_adv is started:
|
||||
|
||||
```
|
||||
I (313) BTDM_INIT: BT controller compile version [2ee0168]
|
||||
I (313) phy_init: phy_version 912,d001756,Jun 2 2022,16:28:07
|
||||
I (353) system_api: Base MAC address is not set
|
||||
I (353) system_api: read default base MAC address from EFUSE
|
||||
I (353) BTDM_INIT: Bluetooth MAC: 84:f7:03:08:14:8e
|
||||
|
||||
I (363) NimBLE_BLE_PERIODIC_ADV: BLE Host Task Started
|
||||
I (373) NimBLE: Device Address:
|
||||
I (373) NimBLE: d0:42:3a:95:84:05
|
||||
I (373) NimBLE:
|
||||
|
||||
I (383) NimBLE: instance 1 started (periodic)
|
||||
```
|
||||
|
||||
## Note
|
||||
* Periodic sync transfer is not implemented for now.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
For any technical queries, please open an [issue](https://github.com/espressif/esp-idf/issues) on GitHub. We will get back to you soon.
|
@ -0,0 +1,4 @@
|
||||
set(srcs "main.c")
|
||||
|
||||
idf_component_register(SRCS "${srcs}"
|
||||
INCLUDE_DIRS ".")
|
@ -0,0 +1,27 @@
|
||||
menu "Example Configuration"
|
||||
|
||||
config EXAMPLE_EXTENDED_ADV
|
||||
bool
|
||||
depends on SOC_BLE_50_SUPPORTED
|
||||
default y if SOC_ESP_NIMBLE_CONTROLLER
|
||||
select BT_NIMBLE_EXT_ADV
|
||||
prompt "Enable Extended Adv"
|
||||
help
|
||||
Use this option to enable extended advertising in the example.
|
||||
If you disable this option, ensure config BT_NIMBLE_EXT_ADV is
|
||||
also disabled from Nimble stack menuconfig.
|
||||
|
||||
config EXAMPLE_RANDOM_ADDR
|
||||
bool
|
||||
prompt "Advertise RANDOM Address"
|
||||
help
|
||||
Use this option to advertise a random address instead of public address
|
||||
|
||||
config EXAMPLE_PERIODIC_ADV_ENH
|
||||
bool
|
||||
prompt "Enable Periodic Adv Enhancements"
|
||||
depends on SOC_BLE_50_SUPPORTED && SOC_BLE_PERIODIC_ADV_ENH_SUPPORTED
|
||||
select BT_NIMBLE_PERIODIC_ADV_ENH
|
||||
help
|
||||
Use this option to enable periodic advertising enhancements
|
||||
endmenu
|
@ -0,0 +1,3 @@
|
||||
dependencies:
|
||||
nimble_peripheral_utils:
|
||||
path: ${IDF_PATH}/examples/bluetooth/nimble/common/nimble_peripheral_utils
|
245
examples/bluetooth/nimble/ble_pawr_adv/ble_pawr_sync/main/main.c
Normal file
245
examples/bluetooth/nimble/ble_pawr_adv/ble_pawr_sync/main/main.c
Normal file
@ -0,0 +1,245 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
#include "esp_log.h"
|
||||
#include "nvs_flash.h"
|
||||
/* BLE */
|
||||
#include "nimble/nimble_port.h"
|
||||
#include "nimble/nimble_port_freertos.h"
|
||||
#include "host/ble_hs.h"
|
||||
#include "host/util/util.h"
|
||||
|
||||
#define TAG "NimBLE_BLE_PAwR"
|
||||
#define TARGET_NAME "Nimble_PAwR"
|
||||
#define BLE_PAWR_RSP_DATA_LEN (20)
|
||||
static uint8_t sub_data_pattern[BLE_PAWR_RSP_DATA_LEN] = {0};
|
||||
|
||||
static int create_periodic_sync(struct ble_gap_ext_disc_desc *disc);
|
||||
static void start_scan(void);
|
||||
|
||||
static struct ble_hs_adv_fields fields;
|
||||
static bool synced = false;
|
||||
|
||||
static int
|
||||
gap_event_cb(struct ble_gap_event *event, void *arg)
|
||||
{
|
||||
int rc;
|
||||
uint8_t *addr;
|
||||
struct ble_gap_ext_disc_desc *disc;
|
||||
|
||||
switch (event->type) {
|
||||
case BLE_GAP_EVENT_EXT_DISC:
|
||||
disc = &event->ext_disc;
|
||||
addr = disc->addr.val;
|
||||
ESP_LOGI(TAG, "[Disc advertiser] addr %02x:%02x:%02x:%02x:%02x:%02x, props: 0x%x, rssi:%d",
|
||||
addr[5], addr[4], addr[3], addr[2], addr[1], addr[0], disc->props, disc->rssi);
|
||||
if (synced) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
rc = ble_hs_adv_parse_fields(&fields, disc->data, disc->length_data);
|
||||
if (rc != 0) {
|
||||
ESP_LOGE(TAG, "Failed to parse adv data, rc = %d", rc);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (disc->periodic_adv_itvl && fields.name_len && !memcmp(fields.name, TARGET_NAME, strlen(TARGET_NAME))) {
|
||||
create_periodic_sync(disc);
|
||||
}
|
||||
return 0;
|
||||
|
||||
case BLE_GAP_EVENT_PERIODIC_REPORT:
|
||||
if (event->periodic_report.event_counter % 10 == 0) {
|
||||
// print every 10th event
|
||||
ESP_LOGI(TAG, "[Periodic Adv Report] handle:%d, rssi:%d, data status:0x%x",
|
||||
event->periodic_report.sync_handle, event->periodic_report.rssi,
|
||||
event->periodic_report.data_status);
|
||||
ESP_LOGI(TAG, "[Periodic Adv Report] event_counter(%d), subevent(%d)",
|
||||
event->periodic_report.event_counter, event->periodic_report.subevent);
|
||||
}
|
||||
|
||||
struct ble_gap_periodic_adv_response_params param = {
|
||||
.request_event = event->periodic_report.event_counter,
|
||||
.request_subevent = event->periodic_report.subevent,
|
||||
.response_subevent = event->periodic_report.subevent,
|
||||
.response_slot = 0
|
||||
};
|
||||
|
||||
struct os_mbuf *data = os_msys_get_pkthdr(BLE_PAWR_RSP_DATA_LEN, 0);
|
||||
if (!data) {
|
||||
ESP_LOGE(TAG, "No memory");
|
||||
return 0;
|
||||
}
|
||||
// create a special data for checking manually in ADV side
|
||||
sub_data_pattern[0] = event->periodic_report.data[0];
|
||||
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);
|
||||
|
||||
rc = ble_gap_periodic_adv_set_response_data(event->periodic_report.sync_handle, ¶m, data);
|
||||
if (rc) {
|
||||
ESP_LOGE(TAG, "Set response data failed, subev(%x), rsp_slot(%d), rc(0x%x)",
|
||||
sub_data_pattern[0], event->periodic_report.subevent, rc);
|
||||
}
|
||||
os_mbuf_free_chain(data);
|
||||
|
||||
return 0;
|
||||
|
||||
case BLE_GAP_EVENT_PERIODIC_SYNC_LOST:
|
||||
ESP_LOGE(TAG, "[Periodic Sync Lost] handle:%d, Reason = 0x%x",
|
||||
event->periodic_sync_lost.sync_handle, event->periodic_sync_lost.reason);
|
||||
synced = false;
|
||||
start_scan();
|
||||
return 0;
|
||||
|
||||
case BLE_GAP_EVENT_PERIODIC_SYNC:
|
||||
if (!event->periodic_sync.status) {
|
||||
ESP_LOGI(TAG, "[Periodic Sync Established] sync handle:%d, num_subevents:0x%x",
|
||||
event->periodic_sync.sync_handle, event->periodic_sync.num_subevents);
|
||||
ESP_LOGI(TAG, "subevent_interval:0x%x, slot_delay:0x%x,slot_spacing:0x%x",
|
||||
event->periodic_sync.subevent_interval,
|
||||
event->periodic_sync.response_slot_delay,
|
||||
event->periodic_sync.response_slot_spacing);
|
||||
ble_gap_disc_cancel();
|
||||
|
||||
// choose subevents in range 0 to (num_subevents - 1)
|
||||
uint8_t subevents[] = {0, 1, 2, 3, 4};
|
||||
int result = ble_gap_periodic_adv_sync_subev(event->periodic_sync.sync_handle, 0, sizeof(subevents), subevents);
|
||||
if (result == ESP_OK) {
|
||||
ESP_LOGI(TAG, "[Subevent Sync OK] sync handle:%d, sync_subevents:%d", event->periodic_sync.sync_handle, sizeof(subevents));
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Failed to sync subevents, rc = 0x%x", result);
|
||||
}
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Periodic Sync Error, status = %d", event->periodic_sync.status);
|
||||
synced = false;
|
||||
start_scan();
|
||||
}
|
||||
return 0;
|
||||
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
create_periodic_sync(struct ble_gap_ext_disc_desc *disc)
|
||||
{
|
||||
int rc;
|
||||
struct ble_gap_periodic_sync_params params;
|
||||
|
||||
params.skip = 0;
|
||||
params.sync_timeout = 4000;
|
||||
params.reports_disabled = 0;
|
||||
|
||||
#if CONFIG_EXAMPLE_PERIODIC_ADV_ENH
|
||||
/* This way the periodic advertising reports will not be
|
||||
delivered to host unless the advertising data is changed
|
||||
or the Data-Id is updated by the advertiser */
|
||||
params.filter_duplicates = 1;
|
||||
#endif
|
||||
|
||||
rc = ble_gap_periodic_adv_sync_create(&disc->addr, disc->sid, ¶ms, gap_event_cb, NULL);
|
||||
if (!rc) {
|
||||
synced = true;
|
||||
ESP_LOGI(TAG, "Create sync");
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Failed to create sync, rc = %d", rc);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void
|
||||
start_scan(void)
|
||||
{
|
||||
int rc;
|
||||
struct ble_gap_ext_disc_params disc_params;
|
||||
|
||||
/* Perform a passive scan. I.e., don't send follow-up scan requests to
|
||||
* each advertiser.
|
||||
*/
|
||||
disc_params.itvl = BLE_GAP_SCAN_ITVL_MS(600);
|
||||
disc_params.window = BLE_GAP_SCAN_ITVL_MS(300);
|
||||
disc_params.passive = 1;
|
||||
|
||||
/* Tell the controller to filter duplicates; we don't want to process
|
||||
* repeated advertisements from the same device.
|
||||
*/
|
||||
rc = ble_gap_ext_disc(BLE_OWN_ADDR_PUBLIC, 0, 0, 1, 0, 0, NULL, &disc_params,
|
||||
gap_event_cb, NULL);
|
||||
if (rc != 0) {
|
||||
ESP_LOGE(TAG, "Error initiating GAP discovery procedure; rc=%d\n", rc);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
on_reset(int reason)
|
||||
{
|
||||
ESP_LOGE(TAG, "Resetting state; reason=%d\n", reason);
|
||||
}
|
||||
|
||||
/* Cnnot find `ble_single_xxxx()`, workaround */
|
||||
// static void
|
||||
// on_sync(void)
|
||||
// {
|
||||
// int ble_single_env_init(void);
|
||||
// int ble_single_init(void);
|
||||
|
||||
// int rc;
|
||||
|
||||
// rc = ble_single_env_init();
|
||||
// assert(!rc);
|
||||
// rc = ble_single_init();
|
||||
// assert(!rc);
|
||||
|
||||
// start_scan();
|
||||
// }
|
||||
|
||||
static void
|
||||
on_sync(void)
|
||||
{
|
||||
int rc;
|
||||
|
||||
/* Make sure we have proper identity address set (public preferred) */
|
||||
rc = ble_hs_util_ensure_addr(0);
|
||||
assert(rc == 0);
|
||||
|
||||
start_scan();
|
||||
}
|
||||
|
||||
void pawr_host_task(void *param)
|
||||
{
|
||||
ESP_LOGI(TAG, "BLE Host Task Started");
|
||||
/* This function will return only when nimble_port_stop() is executed */
|
||||
nimble_port_run();
|
||||
|
||||
nimble_port_freertos_deinit();
|
||||
}
|
||||
|
||||
void
|
||||
app_main(void)
|
||||
{
|
||||
esp_err_t ret;
|
||||
|
||||
/* Initialize NVS — it is used to store PHY calibration data */
|
||||
ret = nvs_flash_init();
|
||||
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
|
||||
ESP_ERROR_CHECK(nvs_flash_erase());
|
||||
ret = nvs_flash_init();
|
||||
}
|
||||
ESP_ERROR_CHECK(ret);
|
||||
|
||||
ret = nimble_port_init();
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to init nimble %d ", ret);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Initialize the NimBLE host configuration. */
|
||||
ble_hs_cfg.reset_cb = on_reset;
|
||||
ble_hs_cfg.sync_cb = on_sync;
|
||||
|
||||
nimble_port_freertos_init(pawr_host_task);
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
# Override some defaults so BT stack is enabled
|
||||
# in this example
|
||||
|
||||
#
|
||||
# BT config
|
||||
#
|
||||
CONFIG_BT_ENABLED=y
|
||||
CONFIG_BT_BLUEDROID_ENABLED=n
|
||||
CONFIG_BT_NIMBLE_ENABLED=y
|
||||
CONFIG_BT_NIMBLE_EXT_ADV=n
|
||||
CONFIG_BT_NIMBLE_PERIODIC_ADV_WITH_RESPONSES=y
|
||||
CONFIG_BT_NIMBLE_MAX_PERIODIC_SYNCS=1
|
||||
CONFIG_BT_NIMBLE_ROLE_CENTRAL=n
|
||||
CONFIG_BT_NIMBLE_ROLE_OBSERVER=y
|
||||
|
||||
CONFIG_BT_CONTROLLER_DISABLED=y
|
||||
|
||||
#
|
||||
# Host-controller Transport
|
||||
#
|
||||
CONFIG_BT_NIMBLE_TRANSPORT_UART_PORT=1
|
||||
CONFIG_UART_BAUDRATE_115200=y
|
||||
CONFIG_BT_NIMBLE_UART_TX_PIN=20
|
||||
CONFIG_BT_NIMBLE_UART_RX_PIN=21
|
||||
CONFIG_UART_HW_FLOWCTRL_CTS_RTS=n
|
||||
# CONFIG_BT_NIMBLE_HCI_UART_RTS_PIN=22
|
||||
# CONFIG_BT_NIMBLE_HCI_UART_CTS_PIN=23
|
||||
# end of Host-controller Transport
|
||||
# end of NimBLE Options
|
||||
|
||||
# C6 Nordic
|
||||
# TX: 20 ---- RX
|
||||
# RX: 21 ---- TX
|
||||
# RTS: 22 ---- CTS
|
||||
# CTS: 23 ---- RTS
|
@ -0,0 +1,320 @@
|
||||
# BLE Periodic Advertisement Example Walkthrough
|
||||
|
||||
## Introduction
|
||||
|
||||
In this tutorial, the ble_periodic_adv example code for the espressif chipsets with BLE5.0 support is reviewed. This example aims at understanding periodic advertisements and related NimBLE APIs.This code implements the periodic advertisement functionality along with extended advertisement by generating a non-resolvable private address.
|
||||
|
||||
## Includes
|
||||
|
||||
This example is located in the examples folder of the ESP-IDF under the [ble_periodic_adv/main](../main). The [main.c](../main/main.c) file located in the main folder contains all the functionality that we are going to review. The header files contained in [main.c](../main/main.c) are:
|
||||
|
||||
```c
|
||||
#include "esp_log.h"
|
||||
#include "nvs_flash.h"
|
||||
/* BLE */
|
||||
#include "nimble/nimble_port.h"
|
||||
#include "nimble/nimble_port_freertos.h"
|
||||
#include "host/ble_hs.h"
|
||||
#include "host/util/util.h"
|
||||
#include "console/console.h"
|
||||
#include "services/gap/ble_svc_gap.h"
|
||||
#include "periodic_adv.h"
|
||||
#include "host/ble_gap.h"
|
||||
#include "host/ble_hs_adv.h"
|
||||
#include "patterns.h"
|
||||
```
|
||||
These `includes` are required for the FreeRTOS and underlying system components to run, including the logging functionality and a library to store data in non-volatile flash memory. We are interested in `“nimble_port.h”`, `“nimble_port_freertos.h”`, `"ble_hs.h"` and `“ble_svc_gap.h”`, `“periodic_adv.h”` which expose the BLE APIs required to implement this example.
|
||||
|
||||
* `nimble_port.h`: Includes the declaration of functions required for the initialization of the nimble stack.
|
||||
* `nimble_port_freertos.h`: Initializes and enables nimble host task.
|
||||
* `ble_hs.h`: Defines the functionalities to handle the host event
|
||||
* `ble_svc_gap.h`:Defines the macros for device name, and device appearance and declares the function to set them.
|
||||
* `periodic_adv.h`:It includes the code containing forward declarations of 2 structs `ble_hs_cfg` , and `ble_gatt_register_ctxt` based on weather macro `H_BLE_PERIODIC_ADV_` is defined.
|
||||
|
||||
## Main Entry Point
|
||||
|
||||
The program’s entry point is the app_main() function:
|
||||
```c
|
||||
void
|
||||
app_main(void)
|
||||
{
|
||||
int rc;
|
||||
/* Initialize NVS — it is used to store PHY calibration data */
|
||||
esp_err_t ret = nvs_flash_init();
|
||||
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
|
||||
ESP_ERROR_CHECK(nvs_flash_erase());
|
||||
ret = nvs_flash_init();
|
||||
}
|
||||
ESP_ERROR_CHECK(ret);
|
||||
|
||||
ret = nimble_port_init();
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(tag, "Failed to init nimble %d ", ret);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Initialize the NimBLE host configuration. */
|
||||
ble_hs_cfg.reset_cb = periodic_adv_on_reset;
|
||||
ble_hs_cfg.sync_cb = periodic_adv_on_sync;
|
||||
ble_hs_cfg.store_status_cb = ble_store_util_status_rr;
|
||||
/* Set the default device name. */
|
||||
rc = ble_svc_gap_device_name_set("nimble_periodic_adv");
|
||||
assert(rc == 0);
|
||||
|
||||
/* XXX Need to have a template for store */
|
||||
ble_store_config_init();
|
||||
|
||||
nimble_port_freertos_init(periodic_adv_host_task);
|
||||
}
|
||||
```
|
||||
The main function starts by initializing the non-volatile storage library. This library allows us to save the key-value pairs in flash memory.`nvs_flash_init()` stores the PHY calibration data. In a Bluetooth Low Energy (BLE) device, cryptographic keys used for encryption and authentication are often stored in Non-Volatile Storage (NVS).BLE stores the peer keys, CCCD keys, peer records, etc on NVS. By storing these keys in NVS, the BLE device can quickly retrieve them when needed, without the need for time-consuming key generations.
|
||||
```c
|
||||
esp_err_t ret = nvs_flash_init();
|
||||
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
|
||||
ESP_ERROR_CHECK(nvs_flash_erase());
|
||||
ret = nvs_flash_init();
|
||||
}
|
||||
ESP_ERROR_CHECK( ret );
|
||||
```
|
||||
|
||||
## BT Controller and Stack Initialization
|
||||
|
||||
The main function calls `nimble_port_init()` to initialize BT Controller and nimble stack. This function initializes the BT controller by first creating its configuration structure named `esp_bt_controller_config_t` with default settings generated by the `BT_CONTROLLER_INIT_CONFIG_DEFAULT()` macro. It implements the Host Controller Interface (HCI) on the controller side, the Link Layer (LL), and the Physical Layer (PHY). The BT Controller is invisible to the user applications and deals with the lower layers of the BLE stack. The controller configuration includes setting the BT controller stack size, priority, and HCI baud rate. With the settings created, the BT controller is initialized and enabled with the `esp_bt_controller_init()` and `esp_bt_controller_enable()` functions:
|
||||
|
||||
```c
|
||||
esp_bt_controller_config_t config_opts = BT_CONTROLLER_INIT_CONFIG_DEFAULT();
|
||||
ret = esp_bt_controller_init(&config_opts);
|
||||
```
|
||||
Next, the controller is enabled in BLE Mode.
|
||||
|
||||
```c
|
||||
ret = esp_bt_controller_enable(ESP_BT_MODE_BLE);
|
||||
```
|
||||
The controller should be enabled in `ESP_BT_MODE_BLE` if you want to use the BLE mode.
|
||||
|
||||
There are four Bluetooth modes supported:
|
||||
|
||||
1. `ESP_BT_MODE_IDLE`: Bluetooth not running
|
||||
2. `ESP_BT_MODE_BLE`: BLE mode
|
||||
3. `ESP_BT_MODE_CLASSIC_BT`: BT Classic mode
|
||||
4. `ESP_BT_MODE_BTDM`: Dual mode (BLE + BT Classic)
|
||||
|
||||
After the initialization of the BT controller, the nimble stack, which includes the common definitions and APIs for BLE, is initialized by using `esp_nimble_init()`:
|
||||
|
||||
```c
|
||||
esp_err_t esp_nimble_init(void)
|
||||
{
|
||||
|
||||
#if !SOC_ESP_NIMBLE_CONTROLLER
|
||||
/* Initialize the function pointers for OS porting */
|
||||
npl_freertos_funcs_init();
|
||||
|
||||
npl_freertos_mempool_init();
|
||||
|
||||
if(esp_nimble_hci_init() != ESP_OK) {
|
||||
ESP_LOGE(NIMBLE_PORT_LOG_TAG, "hci inits failed\n");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
/* Initialize default event queue */
|
||||
ble_npl_eventq_init(&g_eventq_dflt);
|
||||
|
||||
os_msys_init();
|
||||
|
||||
void ble_store_ram_init(void);
|
||||
/* XXX Need to have a template for store */
|
||||
ble_store_ram_init();
|
||||
#endif
|
||||
|
||||
/* Initialize the host */
|
||||
ble_hs_init();
|
||||
return ESP_OK;
|
||||
}
|
||||
```
|
||||
|
||||
The host is configured by setting up the callbacks for Stack-reset, Stack-sync, and Storage status
|
||||
|
||||
```c
|
||||
ble_hs_cfg.reset_cb = periodic_adv_on_reset;
|
||||
ble_hs_cfg.sync_cb = periodic_adv_on_sync;
|
||||
ble_hs_cfg.store_status_cb = ble_store_util_status_rr;
|
||||
```
|
||||
|
||||
The main function calls `ble_svc_gap_device_name_set()` to set the default device name. 'blecent_phy' is passed as the default device name to this function.
|
||||
```c
|
||||
rc = ble_svc_gap_device_name_set("nimble_periodic_adv");
|
||||
```
|
||||
main function calls `ble_store_config_init()` to configure the host by setting up the storage callbacks which handle the read, write, and deletion of security material.
|
||||
```c
|
||||
/* XXX Need to have a template for store */
|
||||
ble_store_config_init();
|
||||
```
|
||||
|
||||
The main function ends by creating a task where nimble will run using `nimble_port_freertos_init()`. This enables the nimble stack by using `esp_nimble_enable()`.
|
||||
|
||||
```c
|
||||
nimble_port_freertos_init(periodic_adv_host_task);
|
||||
```
|
||||
`esp_nimble_enable()` create a task where the nimble host will run. It is not strictly necessary to have a separate task for the nimble host, but to handle the default queue, it is easier to create a separate task.
|
||||
|
||||
## Generation of non-resolvable private address
|
||||
|
||||
In Bluetooth Low Energy (BLE), a non-resolvable private address is a type of Bluetooth device address that is used for privacy purposes. It is a randomly generated address that changes periodically to prevent long-term tracking of a device. The API call to `ble_hs_id_gen_rnd()` is responsible for generating a non-resolvable private address. NRPA is a 48-bit address that is stored in `addr.val`.
|
||||
|
||||
```c
|
||||
#if CONFIG_EXAMPLE_RANDOM_ADDR
|
||||
static void
|
||||
periodic_adv_set_addr(void)
|
||||
{
|
||||
ble_addr_t addr;
|
||||
int rc;
|
||||
|
||||
/* generate new non-resolvable private address */
|
||||
rc = ble_hs_id_gen_rnd(0, &addr);
|
||||
assert(rc == 0);
|
||||
|
||||
/* set generated address */
|
||||
rc = ble_hs_id_set_rnd(addr.val);
|
||||
|
||||
assert(rc == 0);
|
||||
}
|
||||
#endif
|
||||
```
|
||||
|
||||
## Periodic Advertisement
|
||||
|
||||
Periodic advertisement start by creating instances of structures `ble_gap_periodic_adv_params`, `ble_gap_ext_adv_params`, `ble_hs_adv_fields`, and `os_mbuf`. Advertising parameters such as connecting modes, advertising intervals, peer address, advertising-filter policy, etc are defined in these structures for periodic and extended advertisements. `pparams` and `params` instances have parameters for periodic advertisement and extended advertisement respectively.
|
||||
Bluetooth device address is given by the structure ble_aadr_t which contains the fields for address type and address value.
|
||||
|
||||
## Need of Extended Advertisement in Periodic Advertisement
|
||||
|
||||
Non-connectable and non-scannable advertising events containing synchronization information about a periodic advertising train are necessary for the scanner device to sync with the periodic advertising train. The periodic advertising will utilize the same physical layer (PHY) as the auxiliary packet, which is part of the extended advertisement.
|
||||
|
||||
|
||||
Below is the implementation to start periodic advertisement.
|
||||
|
||||
```c
|
||||
static void
|
||||
start_periodic_adv(void)
|
||||
{
|
||||
int rc;
|
||||
struct ble_gap_periodic_adv_params pparams;
|
||||
struct ble_gap_ext_adv_params params;
|
||||
struct ble_hs_adv_fields adv_fields;
|
||||
struct os_mbuf *data;
|
||||
uint8_t instance = 1;
|
||||
ble_addr_t addr;
|
||||
|
||||
/* set random (NRPA) address for instance */
|
||||
rc = ble_hs_id_gen_rnd(1, &addr);
|
||||
assert (rc == 0);
|
||||
|
||||
MODLOG_DFLT(INFO, "Device Address: ");
|
||||
print_addr(addr.val);
|
||||
MODLOG_DFLT(INFO, "\n");
|
||||
|
||||
/* For periodic we use instance with non-connectable advertising */
|
||||
memset (¶ms, 0, sizeof(params));
|
||||
|
||||
/* advertise using random addr */
|
||||
params.own_addr_type = BLE_OWN_ADDR_RANDOM;
|
||||
params.primary_phy = BLE_HCI_LE_PHY_1M;
|
||||
params.secondary_phy = BLE_HCI_LE_PHY_2M;
|
||||
params.sid = 2;
|
||||
|
||||
/* configure instance 1 */
|
||||
rc = ble_gap_ext_adv_configure(instance, ¶ms, NULL, NULL, NULL);
|
||||
assert (rc == 0);
|
||||
|
||||
rc = ble_gap_ext_adv_set_addr(instance, &addr );
|
||||
assert (rc == 0);
|
||||
|
||||
memset(&adv_fields, 0, sizeof(adv_fields));
|
||||
adv_fields.name = (const uint8_t *)"Periodic ADV";
|
||||
adv_fields.name_len = strlen((char *)adv_fields.name);
|
||||
|
||||
/* Default to legacy PDUs size, mbuf chain will be increased if needed
|
||||
*/
|
||||
data = os_msys_get_pkthdr(BLE_HCI_MAX_ADV_DATA_LEN, 0);
|
||||
assert(data);
|
||||
|
||||
rc = ble_hs_adv_set_fields_mbuf(&adv_fields, data);
|
||||
assert(rc == 0);
|
||||
|
||||
rc = ble_gap_ext_adv_set_data(instance, data);
|
||||
assert(rc == 0);
|
||||
|
||||
/* configure periodic advertising */
|
||||
memset(&pparams, 0, sizeof(pparams));
|
||||
pparams.include_tx_power = 0;
|
||||
pparams.itvl_min = BLE_GAP_ADV_ITVL_MS(120);
|
||||
pparams.itvl_max = BLE_GAP_ADV_ITVL_MS(240);
|
||||
|
||||
rc = ble_gap_periodic_adv_configure(instance, &pparams);
|
||||
assert(rc == 0);
|
||||
|
||||
data = os_msys_get_pkthdr(sizeof(periodic_adv_raw_data), 0);
|
||||
assert(data);
|
||||
|
||||
rc = os_mbuf_append(data, periodic_adv_raw_data, sizeof(periodic_adv_raw_data));
|
||||
assert(rc == 0);
|
||||
rc = ble_gap_periodic_adv_set_data(instance, data);
|
||||
assert (rc == 0);
|
||||
|
||||
/* start periodic advertising */
|
||||
assert (rc == 0 rc = ble_gap_periodic_adv_start(instance);
|
||||
);
|
||||
|
||||
/* start advertising */
|
||||
rc = ble_gap_ext_adv_start(instance, 0, 0);
|
||||
assert (rc == 0);
|
||||
|
||||
MODLOG_DFLT(INFO, "instance %u started (periodic)\n", instance);
|
||||
}
|
||||
```
|
||||
|
||||
The periodic advertisement uses a non-connectable advertising mode. `memset (¶ms, 0, sizeof(params))` initialises params to 0. This also sets `params.connectable` to 0.
|
||||
|
||||
## Parameter Configuration
|
||||
|
||||
The below snippets represent the parameter configuration for extended and periodic advertisement.
|
||||
|
||||
### For Extended Advertisement
|
||||
|
||||
```c
|
||||
params.own_addr_type = BLE_OWN_ADDR_RANDOM; //Own address type is set to Random
|
||||
params.primary_phy = BLE_HCI_LE_PHY_1M; // Primary advertising PHY is set to 1M
|
||||
params.secondary_phy = BLE_HCI_LE_PHY_2M; // Secondary advertising PHY is set to 2M
|
||||
params.sid = 2; // Advertising set Id is assigned with value 2.
|
||||
```
|
||||
|
||||
### For Periodic Advertisement
|
||||
|
||||
```c
|
||||
memset(&pparams, 0, sizeof(pparams));
|
||||
pparams.include_tx_power = 0; // Indicates that TX power is not included in advertising PDU
|
||||
pparams.itvl_min = BLE_GAP_ADV_ITVL_MS(120); // Minimum advertising interval of 240ms
|
||||
pparams.itvl_max = BLE_GAP_ADV_ITVL_MS(240); //Maximum advertising interval of 480ms
|
||||
```
|
||||
|
||||
Periodic advertisement is started for a particular advertisement instance by calling the API `ble_gap_periodic_adv_start(instance)`. This function takes instance-id as an input parameter. It defines the hci command by initializing the command parameters which are represented in the following lines.
|
||||
|
||||
```c
|
||||
struct ble_hci_le_set_periodic_adv_enable_cp cmd;
|
||||
cmd.enable = 0x01;
|
||||
cmd.adv_handle = instance;
|
||||
```
|
||||
|
||||
Extended advertising is invoked for a particular instance using the API call `ble_gap_ext_adv_start(instance, 0, 0)`.Instance-id, duration, and max_events are input parameters for this API call respectively.
|
||||
|
||||
Duration represents the time for which the adverteiment will take place. Upon expiration, the advertising procedure ends, and the BLE_GAP_EVENT_ADV_COMPLETE event is reported.0 value is used for no expiration.
|
||||
|
||||
max_events Number of advertising events that should be sent before advertising ends and a BLE_GAP_EVENT_ADV_COMPLETE event is reported.0 value is used for no limit.
|
||||
|
||||
## Conclusion
|
||||
|
||||
This Walkthrough covers the code explanation of the BLE_PERIODIC_ADV. The following points are concluded through this walkthrough.
|
||||
|
||||
1. Periodic advertising allows the scanner to sync with the advertiser so the scanner and advertiser would wake up at the same time.
|
||||
2. Periodic advertisement uses NRPA (Non Resolvable private address). It is a randomly generated address that changes periodically to prevent long-term tracking of a device.
|
||||
3. Extended advertising is used to indicate to the scanner that the advertiser is utilizing periodic advertising. Therefore, periodic advertising is started before extended advertising so that the scanner and advertiser can synchronize their actions and operate at the same time.
|
@ -0,0 +1,6 @@
|
||||
# The following lines of boilerplate have to be in your project's
|
||||
# CMakeLists in this exact order for cmake to work correctly
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(ble_pawr_adv_conn)
|
@ -0,0 +1,75 @@
|
||||
| Supported Targets | ESP32-C6 |
|
||||
| ----------------- | -------- |
|
||||
|
||||
# BLE Periodic Advertiser Example
|
||||
|
||||
(See the README.md file in the upper level 'examples' directory for more information about examples.)
|
||||
|
||||
This example starts periodic advertising with non resolvable private address.
|
||||
|
||||
It uses Bluetooth controller and NimBLE stack based BLE host.
|
||||
|
||||
This example aims at understanding periodic advertisement and related NimBLE APIs.
|
||||
|
||||
|
||||
To test this demo, any BLE Periodic Sync app can be used.
|
||||
|
||||
|
||||
Note :
|
||||
|
||||
* Make sure to run `python -m pip install --user -r $IDF_PATH/requirements.txt -r $IDF_PATH/tools/ble/requirements.txt` to install the dependency packages needed.
|
||||
* Currently this Python utility is only supported on Linux (BLE communication is via BLuez + DBus).
|
||||
|
||||
## How to Use Example
|
||||
|
||||
Before project configuration and build, be sure to set the correct chip target using:
|
||||
|
||||
```bash
|
||||
idf.py set-target <chip_name>
|
||||
```
|
||||
|
||||
### Configure the project
|
||||
|
||||
Open the project configuration menu:
|
||||
|
||||
```bash
|
||||
idf.py menuconfig
|
||||
```
|
||||
|
||||
In the `Example Configuration` menu:
|
||||
|
||||
* Select I/O capabilities of device from `Example Configuration --> I/O Capability`, default is `Just_works`.
|
||||
|
||||
### Build and Flash
|
||||
|
||||
Run `idf.py -p PORT flash monitor` to build, flash and monitor the project.
|
||||
|
||||
(To exit the serial monitor, type ``Ctrl-]``.)
|
||||
|
||||
See the [Getting Started Guide](https://idf.espressif.com/) for full steps to configure and use ESP-IDF to build projects.
|
||||
|
||||
## Example Output
|
||||
|
||||
There is this console output when periodic_adv is started:
|
||||
|
||||
```
|
||||
I (313) BTDM_INIT: BT controller compile version [2ee0168]
|
||||
I (313) phy_init: phy_version 912,d001756,Jun 2 2022,16:28:07
|
||||
I (353) system_api: Base MAC address is not set
|
||||
I (353) system_api: read default base MAC address from EFUSE
|
||||
I (353) BTDM_INIT: Bluetooth MAC: 84:f7:03:08:14:8e
|
||||
|
||||
I (363) NimBLE_BLE_PERIODIC_ADV: BLE Host Task Started
|
||||
I (373) NimBLE: Device Address:
|
||||
I (373) NimBLE: d0:42:3a:95:84:05
|
||||
I (373) NimBLE:
|
||||
|
||||
I (383) NimBLE: instance 1 started (periodic)
|
||||
```
|
||||
|
||||
## Note
|
||||
* Periodic sync transfer is not implemented for now.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
For any technical queries, please open an [issue](https://github.com/espressif/esp-idf/issues) on GitHub. We will get back to you soon.
|
@ -0,0 +1,4 @@
|
||||
set(srcs "main.c")
|
||||
|
||||
idf_component_register(SRCS "${srcs}"
|
||||
INCLUDE_DIRS ".")
|
@ -0,0 +1,27 @@
|
||||
menu "Example Configuration"
|
||||
|
||||
config EXAMPLE_EXTENDED_ADV
|
||||
bool
|
||||
depends on SOC_BLE_50_SUPPORTED
|
||||
default y if SOC_ESP_NIMBLE_CONTROLLER
|
||||
select BT_NIMBLE_EXT_ADV
|
||||
prompt "Enable Extended Adv"
|
||||
help
|
||||
Use this option to enable extended advertising in the example.
|
||||
If you disable this option, ensure config BT_NIMBLE_EXT_ADV is
|
||||
also disabled from Nimble stack menuconfig.
|
||||
|
||||
config EXAMPLE_RANDOM_ADDR
|
||||
bool
|
||||
prompt "Advertise RANDOM Address"
|
||||
help
|
||||
Use this option to advertise a random address instead of public address
|
||||
|
||||
config EXAMPLE_PERIODIC_ADV_ENH
|
||||
bool
|
||||
prompt "Enable Periodic Adv Enhancements"
|
||||
depends on SOC_BLE_50_SUPPORTED && SOC_BLE_PERIODIC_ADV_ENH_SUPPORTED
|
||||
select BT_NIMBLE_PERIODIC_ADV_ENH
|
||||
help
|
||||
Use this option to enable periodic advertising enhancements
|
||||
endmenu
|
@ -0,0 +1,3 @@
|
||||
dependencies:
|
||||
nimble_peripheral_utils:
|
||||
path: ${IDF_PATH}/examples/bluetooth/nimble/common/nimble_peripheral_utils
|
@ -0,0 +1,285 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
#include "esp_log.h"
|
||||
#include "nvs_flash.h"
|
||||
/* BLE */
|
||||
#include "nimble/nimble_port.h"
|
||||
#include "nimble/nimble_port_freertos.h"
|
||||
#include "host/ble_hs.h"
|
||||
|
||||
#define BLE_PAWR_NUM_SUBEVTS (5)
|
||||
#define BLE_PAWR_SUB_INTERVAL (12) /*!< Interval between subevents (N * 1.25 ms) */
|
||||
#define BLE_PAWR_RSP_SLOT_DELAY (1) /*!< The first response slot delay (N * 1.25 ms)*/
|
||||
#define BLE_PAWR_RSP_SLOT_SPACING (32) /*!< Time between response slots (N * 0.125 ms) */
|
||||
#define BLE_PAWR_NUM_RSP_SLOTS (3) /*!< Number of subevent response slots */
|
||||
#define BLE_PAWR_SUB_DATA_LEN (10)
|
||||
|
||||
#define TAG "NimBLE_BLE_PAwR"
|
||||
|
||||
static struct ble_gap_set_periodic_adv_subev_data_params sub_data_params[BLE_PAWR_NUM_SUBEVTS];
|
||||
static uint8_t sub_data_pattern[BLE_PAWR_SUB_DATA_LEN] = {0};
|
||||
static uint8_t conn;
|
||||
static struct ble_gap_conn_desc desc;
|
||||
char *
|
||||
addr_str(const void *addr)
|
||||
{
|
||||
static char buf[6 * 2 + 5 + 1];
|
||||
const uint8_t *u8p;
|
||||
|
||||
u8p = addr;
|
||||
sprintf(buf, "%02x:%02x:%02x:%02x:%02x:%02x",
|
||||
u8p[5], u8p[4], u8p[3], u8p[2], u8p[1], u8p[0]);
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
void
|
||||
print_conn_desc(const struct ble_gap_conn_desc *desc)
|
||||
{
|
||||
ESP_LOGI(TAG,"handle=%d our_ota_addr_type=%d our_ota_addr=%s ",
|
||||
desc->conn_handle, desc->our_ota_addr.type,
|
||||
addr_str(desc->our_ota_addr.val));
|
||||
ESP_LOGI(TAG, "our_id_addr_type=%d our_id_addr=%s ",
|
||||
desc->our_id_addr.type, addr_str(desc->our_id_addr.val));
|
||||
ESP_LOGI(TAG, "peer_ota_addr_type=%d peer_ota_addr=%s ",
|
||||
desc->peer_ota_addr.type, addr_str(desc->peer_ota_addr.val));
|
||||
ESP_LOGI(TAG, "peer_id_addr_type=%d peer_id_addr=%s ",
|
||||
desc->peer_id_addr.type, addr_str(desc->peer_id_addr.val));
|
||||
ESP_LOGI(TAG, "conn_itvl=%d conn_latency=%d supervision_timeout=%d "
|
||||
"encrypted=%d authenticated=%d bonded=%d\n",
|
||||
desc->conn_itvl, desc->conn_latency,
|
||||
desc->supervision_timeout,
|
||||
desc->sec_state.encrypted,
|
||||
desc->sec_state.authenticated,
|
||||
desc->sec_state.bonded);
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
gap_event_cb(struct ble_gap_event *event, void *arg)
|
||||
{
|
||||
int rc;
|
||||
uint8_t sub;
|
||||
uint8_t sent_num;
|
||||
struct os_mbuf *data;
|
||||
ble_addr_t peer_addr;
|
||||
uint8_t adv_handle;
|
||||
uint8_t subevent;
|
||||
uint8_t phy_mask;
|
||||
|
||||
switch (event->type) {
|
||||
case BLE_GAP_EVENT_CONNECT:
|
||||
printf("\n");
|
||||
ESP_LOGI(TAG, "Connection established, conn_handle = 0x%02x, Adv handle = 0x%0x, status = 0x%0x\n",event->connect.conn_handle,event->connect.adv_handle, event->connect.status);
|
||||
rc = ble_gap_conn_find(event->connect.conn_handle, &desc);
|
||||
if(rc == 0){
|
||||
print_conn_desc(&desc);
|
||||
}
|
||||
else{
|
||||
ESP_LOGE(TAG,"Failed to find Conn Information");
|
||||
}
|
||||
|
||||
return 0;
|
||||
case BLE_GAP_EVENT_PER_SUBEV_DATA_REQ:
|
||||
ESP_LOGI(TAG, "[Request] data: %x, subevt start:%d, subevt count:%d",
|
||||
sub_data_pattern[0],
|
||||
event->periodic_adv_subev_data_req.subevent_start,
|
||||
event->periodic_adv_subev_data_req.subevent_data_count);
|
||||
|
||||
sent_num = event->periodic_adv_subev_data_req.subevent_data_count;
|
||||
for (uint8_t i = 0; i < sent_num; i++) {
|
||||
data = os_msys_get_pkthdr(BLE_PAWR_SUB_DATA_LEN, 0);
|
||||
if (!data) {
|
||||
ESP_LOGE(TAG, "No memory, %d", i);
|
||||
break;
|
||||
}
|
||||
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);
|
||||
os_mbuf_append(data, sub_data_pattern, BLE_PAWR_SUB_DATA_LEN);
|
||||
sub_data_params[i].subevent = sub;
|
||||
sub_data_params[i].response_slot_start = 0;
|
||||
sub_data_params[i].response_slot_count = BLE_PAWR_NUM_RSP_SLOTS;
|
||||
sub_data_params[i].data = data;
|
||||
sub_data_pattern[0]++;
|
||||
}
|
||||
|
||||
rc = ble_gap_set_periodic_adv_subev_data(event->periodic_adv_subev_data_req.adv_handle,
|
||||
sent_num, sub_data_params);
|
||||
if (rc) {
|
||||
ESP_LOGE(TAG, "Failed to set Subevent Data, rc = 0x%x", rc);
|
||||
}
|
||||
return 0;
|
||||
|
||||
case BLE_GAP_EVENT_PER_SUBEV_RESP:
|
||||
|
||||
if (event->periodic_adv_response.data_status == BLE_GAP_PER_ADV_DATA_STATUS_INCOMPLETE) {
|
||||
// ESP_LOGI(TAG,"Incomplete response report received, discarding \n");
|
||||
}
|
||||
else if (event->periodic_adv_response.data_status == BLE_GAP_PER_ADV_DATA_STATUS_RX_FAILED) {
|
||||
// ESP_LOGI(TAG,"Controller failed to received the AUX_SYNC_SUBEVENT_RSP\n");
|
||||
}
|
||||
else if (event->periodic_adv_response.data_status == BLE_GAP_PER_ADV_DATA_STATUS_COMPLETE) {
|
||||
ESP_LOGI(TAG, "[Response] subevent:%d, response_slot:%d, data_length:%d, data:%x",
|
||||
event->periodic_adv_response.subevent,
|
||||
event->periodic_adv_response.response_slot,
|
||||
event->periodic_adv_response.data_length,
|
||||
event->periodic_adv_response.data[0]);
|
||||
peer_addr.type=0;
|
||||
memcpy(peer_addr.val,&event->periodic_adv_response.data[1],6);
|
||||
|
||||
adv_handle = event->periodic_adv_response.adv_handle;
|
||||
subevent = event->periodic_adv_response.subevent;
|
||||
phy_mask = 0x01;
|
||||
|
||||
if (conn == 0) {
|
||||
rc = ble_gap_connect_with_synced(0,adv_handle,subevent,&peer_addr,30000,phy_mask,NULL,NULL,NULL,gap_event_cb,NULL);
|
||||
if (rc != 0 ) {
|
||||
ESP_LOGI(TAG,"Error: Failed to connect to device , rc = %d\n",rc);
|
||||
}
|
||||
conn = 1;
|
||||
}
|
||||
}
|
||||
else if (event->periodic_adv_response.data_status == BLE_GAP_PER_ADV_DATA_STATUS_TRUNCATED) {
|
||||
// ESP_LOGI(TAG,"Truncated response report received, discarding\n");
|
||||
}
|
||||
else {
|
||||
ESP_LOGE(TAG,"Invalid data status\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
start_periodic_adv(void)
|
||||
{
|
||||
int rc;
|
||||
uint8_t addr[6];
|
||||
struct ble_gap_periodic_adv_params pparams;
|
||||
struct ble_gap_ext_adv_params params;
|
||||
struct ble_hs_adv_fields adv_fields;
|
||||
struct os_mbuf *data;
|
||||
uint8_t instance = 0;
|
||||
|
||||
#if MYNEWT_VAL(BLE_PERIODIC_ADV_ENH)
|
||||
struct ble_gap_periodic_adv_enable_params eparams;
|
||||
memset(&eparams, 0, sizeof(eparams));
|
||||
#endif
|
||||
|
||||
/* Get the local public address. */
|
||||
rc = ble_hs_id_copy_addr(BLE_ADDR_PUBLIC, addr, NULL);
|
||||
assert (rc == 0);
|
||||
|
||||
ESP_LOGI(TAG, "Device Address %02x:%02x:%02x:%02x:%02x:%02x", addr[5], addr[4], addr[3],
|
||||
addr[2], addr[1], addr[0]);
|
||||
|
||||
/* For periodic we use instance with non-connectable advertising */
|
||||
memset (¶ms, 0, sizeof(params));
|
||||
params.own_addr_type = BLE_OWN_ADDR_PUBLIC;
|
||||
params.primary_phy = BLE_HCI_LE_PHY_CODED;
|
||||
params.secondary_phy = BLE_HCI_LE_PHY_1M;
|
||||
params.sid = 0;
|
||||
params.itvl_min = BLE_GAP_ADV_ITVL_MS(100);
|
||||
params.itvl_max = BLE_GAP_ADV_ITVL_MS(100);
|
||||
|
||||
rc = ble_gap_ext_adv_configure(instance, ¶ms, NULL, gap_event_cb, NULL);
|
||||
assert (rc == 0);
|
||||
|
||||
memset(&adv_fields, 0, sizeof(adv_fields));
|
||||
adv_fields.name = (const uint8_t *)"Nimble_PAwR";
|
||||
adv_fields.name_len = strlen((char *)adv_fields.name);
|
||||
|
||||
/* mbuf chain will be increased if needed */
|
||||
data = os_msys_get_pkthdr(BLE_HCI_MAX_ADV_DATA_LEN, 0);
|
||||
assert(data);
|
||||
|
||||
rc = ble_hs_adv_set_fields_mbuf(&adv_fields, data);
|
||||
assert(rc == 0);
|
||||
|
||||
rc = ble_gap_ext_adv_set_data(instance, data);
|
||||
assert(rc == 0);
|
||||
|
||||
/* configure periodic advertising */
|
||||
memset(&pparams, 0, sizeof(pparams));
|
||||
pparams.include_tx_power = 0;
|
||||
pparams.itvl_min = BLE_GAP_PERIODIC_ITVL_MS(3000);
|
||||
pparams.itvl_max = BLE_GAP_PERIODIC_ITVL_MS(3000);
|
||||
/* Configure the parameters of PAwR. */
|
||||
pparams.num_subevents = BLE_PAWR_NUM_SUBEVTS;
|
||||
pparams.subevent_interval = BLE_GAP_PERIODIC_ITVL_MS(300);
|
||||
pparams.response_slot_delay = BLE_GAP_PERIODIC_ITVL_MS(80);
|
||||
pparams.response_slot_spacing = 0xFF;
|
||||
pparams.num_response_slots = BLE_PAWR_NUM_RSP_SLOTS;
|
||||
|
||||
rc = ble_gap_periodic_adv_configure(instance, &pparams);
|
||||
assert(rc == 0);
|
||||
|
||||
/* start periodic advertising */
|
||||
#if MYNEWT_VAL(BLE_PERIODIC_ADV_ENH)
|
||||
eparams.include_adi = 1;
|
||||
rc = ble_gap_periodic_adv_start(instance, &eparams);
|
||||
#else
|
||||
rc = ble_gap_periodic_adv_start(instance);
|
||||
#endif
|
||||
assert (rc == 0);
|
||||
|
||||
/* start advertising */
|
||||
rc = ble_gap_ext_adv_start(instance, 0, 0);
|
||||
assert (rc == 0);
|
||||
|
||||
ESP_LOGI(TAG, "instance %u started (periodic)\n", instance);
|
||||
}
|
||||
|
||||
static void
|
||||
on_reset(int reason)
|
||||
{
|
||||
ESP_LOGE(TAG, "Resetting state; reason=%d\n", reason);
|
||||
}
|
||||
|
||||
static void
|
||||
on_sync(void)
|
||||
{
|
||||
/* Begin advertising. */
|
||||
start_periodic_adv();
|
||||
}
|
||||
|
||||
void pawr_host_task(void *param)
|
||||
{
|
||||
ESP_LOGI(TAG, "BLE Host Task Started");
|
||||
/* This function will return only when nimble_port_stop() is executed */
|
||||
nimble_port_run();
|
||||
|
||||
nimble_port_freertos_deinit();
|
||||
}
|
||||
|
||||
void
|
||||
app_main(void)
|
||||
{
|
||||
esp_err_t ret;
|
||||
|
||||
/* Initialize NVS — it is used to store PHY calibration data */
|
||||
ret = nvs_flash_init();
|
||||
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
|
||||
ESP_ERROR_CHECK(nvs_flash_erase());
|
||||
ret = nvs_flash_init();
|
||||
}
|
||||
ESP_ERROR_CHECK(ret);
|
||||
|
||||
ret = nimble_port_init();
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to init nimble %d ", ret);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Initialize the NimBLE host configuration. */
|
||||
ble_hs_cfg.reset_cb = on_reset;
|
||||
ble_hs_cfg.sync_cb = on_sync;
|
||||
|
||||
nimble_port_freertos_init(pawr_host_task);
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
# Override some defaults so BT stack is enabled
|
||||
# in this example
|
||||
|
||||
#
|
||||
# BT config
|
||||
#
|
||||
CONFIG_BT_ENABLED=y
|
||||
CONFIG_BT_BLUEDROID_ENABLED=n
|
||||
CONFIG_BT_NIMBLE_ENABLED=y
|
||||
CONFIG_BT_NIMBLE_EXT_ADV=y
|
||||
CONFIG_BT_NIMBLE_PERIODIC_ADV_WITH_RESPONSES=y
|
||||
CONFIG_BT_NIMBLE_MAX_PERIODIC_SYNCS=1
|
||||
CONFIG_BT_NIMBLE_ROLE_CENTRAL=n
|
||||
CONFIG_BT_NIMBLE_ROLE_OBSERVER=n
|
||||
|
||||
CONFIG_BT_CONTROLLER_DISABLED=y
|
||||
|
||||
#
|
||||
# Host-controller Transport
|
||||
#
|
||||
CONFIG_BT_NIMBLE_TRANSPORT_UART_PORT=1
|
||||
CONFIG_UART_BAUDRATE_115200=y
|
||||
CONFIG_BT_NIMBLE_UART_TX_PIN=20
|
||||
CONFIG_BT_NIMBLE_UART_RX_PIN=21
|
||||
CONFIG_UART_HW_FLOWCTRL_CTS_RTS=n
|
||||
# CONFIG_BT_NIMBLE_HCI_UART_RTS_PIN=22
|
||||
# CONFIG_BT_NIMBLE_HCI_UART_CTS_PIN=23
|
||||
# end of Host-controller Transport
|
||||
# end of NimBLE Options
|
||||
|
||||
# C6 Nordic
|
||||
# TX: 20 ---- RX
|
||||
# RX: 21 ---- TX
|
||||
# RTS: 22 ---- CTS
|
||||
# CTS: 23 ---- RTS
|
@ -0,0 +1,320 @@
|
||||
# BLE Periodic Advertisement Example Walkthrough
|
||||
|
||||
## Introduction
|
||||
|
||||
In this tutorial, the ble_periodic_adv example code for the espressif chipsets with BLE5.0 support is reviewed. This example aims at understanding periodic advertisements and related NimBLE APIs.This code implements the periodic advertisement functionality along with extended advertisement by generating a non-resolvable private address.
|
||||
|
||||
## Includes
|
||||
|
||||
This example is located in the examples folder of the ESP-IDF under the [ble_periodic_adv/main](../main). The [main.c](../main/main.c) file located in the main folder contains all the functionality that we are going to review. The header files contained in [main.c](../main/main.c) are:
|
||||
|
||||
```c
|
||||
#include "esp_log.h"
|
||||
#include "nvs_flash.h"
|
||||
/* BLE */
|
||||
#include "nimble/nimble_port.h"
|
||||
#include "nimble/nimble_port_freertos.h"
|
||||
#include "host/ble_hs.h"
|
||||
#include "host/util/util.h"
|
||||
#include "console/console.h"
|
||||
#include "services/gap/ble_svc_gap.h"
|
||||
#include "periodic_adv.h"
|
||||
#include "host/ble_gap.h"
|
||||
#include "host/ble_hs_adv.h"
|
||||
#include "patterns.h"
|
||||
```
|
||||
These `includes` are required for the FreeRTOS and underlying system components to run, including the logging functionality and a library to store data in non-volatile flash memory. We are interested in `“nimble_port.h”`, `“nimble_port_freertos.h”`, `"ble_hs.h"` and `“ble_svc_gap.h”`, `“periodic_adv.h”` which expose the BLE APIs required to implement this example.
|
||||
|
||||
* `nimble_port.h`: Includes the declaration of functions required for the initialization of the nimble stack.
|
||||
* `nimble_port_freertos.h`: Initializes and enables nimble host task.
|
||||
* `ble_hs.h`: Defines the functionalities to handle the host event
|
||||
* `ble_svc_gap.h`:Defines the macros for device name, and device appearance and declares the function to set them.
|
||||
* `periodic_adv.h`:It includes the code containing forward declarations of 2 structs `ble_hs_cfg` , and `ble_gatt_register_ctxt` based on weather macro `H_BLE_PERIODIC_ADV_` is defined.
|
||||
|
||||
## Main Entry Point
|
||||
|
||||
The program’s entry point is the app_main() function:
|
||||
```c
|
||||
void
|
||||
app_main(void)
|
||||
{
|
||||
int rc;
|
||||
/* Initialize NVS — it is used to store PHY calibration data */
|
||||
esp_err_t ret = nvs_flash_init();
|
||||
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
|
||||
ESP_ERROR_CHECK(nvs_flash_erase());
|
||||
ret = nvs_flash_init();
|
||||
}
|
||||
ESP_ERROR_CHECK(ret);
|
||||
|
||||
ret = nimble_port_init();
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(tag, "Failed to init nimble %d ", ret);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Initialize the NimBLE host configuration. */
|
||||
ble_hs_cfg.reset_cb = periodic_adv_on_reset;
|
||||
ble_hs_cfg.sync_cb = periodic_adv_on_sync;
|
||||
ble_hs_cfg.store_status_cb = ble_store_util_status_rr;
|
||||
/* Set the default device name. */
|
||||
rc = ble_svc_gap_device_name_set("nimble_periodic_adv");
|
||||
assert(rc == 0);
|
||||
|
||||
/* XXX Need to have a template for store */
|
||||
ble_store_config_init();
|
||||
|
||||
nimble_port_freertos_init(periodic_adv_host_task);
|
||||
}
|
||||
```
|
||||
The main function starts by initializing the non-volatile storage library. This library allows us to save the key-value pairs in flash memory.`nvs_flash_init()` stores the PHY calibration data. In a Bluetooth Low Energy (BLE) device, cryptographic keys used for encryption and authentication are often stored in Non-Volatile Storage (NVS).BLE stores the peer keys, CCCD keys, peer records, etc on NVS. By storing these keys in NVS, the BLE device can quickly retrieve them when needed, without the need for time-consuming key generations.
|
||||
```c
|
||||
esp_err_t ret = nvs_flash_init();
|
||||
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
|
||||
ESP_ERROR_CHECK(nvs_flash_erase());
|
||||
ret = nvs_flash_init();
|
||||
}
|
||||
ESP_ERROR_CHECK( ret );
|
||||
```
|
||||
|
||||
## BT Controller and Stack Initialization
|
||||
|
||||
The main function calls `nimble_port_init()` to initialize BT Controller and nimble stack. This function initializes the BT controller by first creating its configuration structure named `esp_bt_controller_config_t` with default settings generated by the `BT_CONTROLLER_INIT_CONFIG_DEFAULT()` macro. It implements the Host Controller Interface (HCI) on the controller side, the Link Layer (LL), and the Physical Layer (PHY). The BT Controller is invisible to the user applications and deals with the lower layers of the BLE stack. The controller configuration includes setting the BT controller stack size, priority, and HCI baud rate. With the settings created, the BT controller is initialized and enabled with the `esp_bt_controller_init()` and `esp_bt_controller_enable()` functions:
|
||||
|
||||
```c
|
||||
esp_bt_controller_config_t config_opts = BT_CONTROLLER_INIT_CONFIG_DEFAULT();
|
||||
ret = esp_bt_controller_init(&config_opts);
|
||||
```
|
||||
Next, the controller is enabled in BLE Mode.
|
||||
|
||||
```c
|
||||
ret = esp_bt_controller_enable(ESP_BT_MODE_BLE);
|
||||
```
|
||||
The controller should be enabled in `ESP_BT_MODE_BLE` if you want to use the BLE mode.
|
||||
|
||||
There are four Bluetooth modes supported:
|
||||
|
||||
1. `ESP_BT_MODE_IDLE`: Bluetooth not running
|
||||
2. `ESP_BT_MODE_BLE`: BLE mode
|
||||
3. `ESP_BT_MODE_CLASSIC_BT`: BT Classic mode
|
||||
4. `ESP_BT_MODE_BTDM`: Dual mode (BLE + BT Classic)
|
||||
|
||||
After the initialization of the BT controller, the nimble stack, which includes the common definitions and APIs for BLE, is initialized by using `esp_nimble_init()`:
|
||||
|
||||
```c
|
||||
esp_err_t esp_nimble_init(void)
|
||||
{
|
||||
|
||||
#if !SOC_ESP_NIMBLE_CONTROLLER
|
||||
/* Initialize the function pointers for OS porting */
|
||||
npl_freertos_funcs_init();
|
||||
|
||||
npl_freertos_mempool_init();
|
||||
|
||||
if(esp_nimble_hci_init() != ESP_OK) {
|
||||
ESP_LOGE(NIMBLE_PORT_LOG_TAG, "hci inits failed\n");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
/* Initialize default event queue */
|
||||
ble_npl_eventq_init(&g_eventq_dflt);
|
||||
|
||||
os_msys_init();
|
||||
|
||||
void ble_store_ram_init(void);
|
||||
/* XXX Need to have a template for store */
|
||||
ble_store_ram_init();
|
||||
#endif
|
||||
|
||||
/* Initialize the host */
|
||||
ble_hs_init();
|
||||
return ESP_OK;
|
||||
}
|
||||
```
|
||||
|
||||
The host is configured by setting up the callbacks for Stack-reset, Stack-sync, and Storage status
|
||||
|
||||
```c
|
||||
ble_hs_cfg.reset_cb = periodic_adv_on_reset;
|
||||
ble_hs_cfg.sync_cb = periodic_adv_on_sync;
|
||||
ble_hs_cfg.store_status_cb = ble_store_util_status_rr;
|
||||
```
|
||||
|
||||
The main function calls `ble_svc_gap_device_name_set()` to set the default device name. 'blecent_phy' is passed as the default device name to this function.
|
||||
```c
|
||||
rc = ble_svc_gap_device_name_set("nimble_periodic_adv");
|
||||
```
|
||||
main function calls `ble_store_config_init()` to configure the host by setting up the storage callbacks which handle the read, write, and deletion of security material.
|
||||
```c
|
||||
/* XXX Need to have a template for store */
|
||||
ble_store_config_init();
|
||||
```
|
||||
|
||||
The main function ends by creating a task where nimble will run using `nimble_port_freertos_init()`. This enables the nimble stack by using `esp_nimble_enable()`.
|
||||
|
||||
```c
|
||||
nimble_port_freertos_init(periodic_adv_host_task);
|
||||
```
|
||||
`esp_nimble_enable()` create a task where the nimble host will run. It is not strictly necessary to have a separate task for the nimble host, but to handle the default queue, it is easier to create a separate task.
|
||||
|
||||
## Generation of non-resolvable private address
|
||||
|
||||
In Bluetooth Low Energy (BLE), a non-resolvable private address is a type of Bluetooth device address that is used for privacy purposes. It is a randomly generated address that changes periodically to prevent long-term tracking of a device. The API call to `ble_hs_id_gen_rnd()` is responsible for generating a non-resolvable private address. NRPA is a 48-bit address that is stored in `addr.val`.
|
||||
|
||||
```c
|
||||
#if CONFIG_EXAMPLE_RANDOM_ADDR
|
||||
static void
|
||||
periodic_adv_set_addr(void)
|
||||
{
|
||||
ble_addr_t addr;
|
||||
int rc;
|
||||
|
||||
/* generate new non-resolvable private address */
|
||||
rc = ble_hs_id_gen_rnd(0, &addr);
|
||||
assert(rc == 0);
|
||||
|
||||
/* set generated address */
|
||||
rc = ble_hs_id_set_rnd(addr.val);
|
||||
|
||||
assert(rc == 0);
|
||||
}
|
||||
#endif
|
||||
```
|
||||
|
||||
## Periodic Advertisement
|
||||
|
||||
Periodic advertisement start by creating instances of structures `ble_gap_periodic_adv_params`, `ble_gap_ext_adv_params`, `ble_hs_adv_fields`, and `os_mbuf`. Advertising parameters such as connecting modes, advertising intervals, peer address, advertising-filter policy, etc are defined in these structures for periodic and extended advertisements. `pparams` and `params` instances have parameters for periodic advertisement and extended advertisement respectively.
|
||||
Bluetooth device address is given by the structure ble_aadr_t which contains the fields for address type and address value.
|
||||
|
||||
## Need of Extended Advertisement in Periodic Advertisement
|
||||
|
||||
Non-connectable and non-scannable advertising events containing synchronization information about a periodic advertising train are necessary for the scanner device to sync with the periodic advertising train. The periodic advertising will utilize the same physical layer (PHY) as the auxiliary packet, which is part of the extended advertisement.
|
||||
|
||||
|
||||
Below is the implementation to start periodic advertisement.
|
||||
|
||||
```c
|
||||
static void
|
||||
start_periodic_adv(void)
|
||||
{
|
||||
int rc;
|
||||
struct ble_gap_periodic_adv_params pparams;
|
||||
struct ble_gap_ext_adv_params params;
|
||||
struct ble_hs_adv_fields adv_fields;
|
||||
struct os_mbuf *data;
|
||||
uint8_t instance = 1;
|
||||
ble_addr_t addr;
|
||||
|
||||
/* set random (NRPA) address for instance */
|
||||
rc = ble_hs_id_gen_rnd(1, &addr);
|
||||
assert (rc == 0);
|
||||
|
||||
MODLOG_DFLT(INFO, "Device Address: ");
|
||||
print_addr(addr.val);
|
||||
MODLOG_DFLT(INFO, "\n");
|
||||
|
||||
/* For periodic we use instance with non-connectable advertising */
|
||||
memset (¶ms, 0, sizeof(params));
|
||||
|
||||
/* advertise using random addr */
|
||||
params.own_addr_type = BLE_OWN_ADDR_RANDOM;
|
||||
params.primary_phy = BLE_HCI_LE_PHY_1M;
|
||||
params.secondary_phy = BLE_HCI_LE_PHY_2M;
|
||||
params.sid = 2;
|
||||
|
||||
/* configure instance 1 */
|
||||
rc = ble_gap_ext_adv_configure(instance, ¶ms, NULL, NULL, NULL);
|
||||
assert (rc == 0);
|
||||
|
||||
rc = ble_gap_ext_adv_set_addr(instance, &addr );
|
||||
assert (rc == 0);
|
||||
|
||||
memset(&adv_fields, 0, sizeof(adv_fields));
|
||||
adv_fields.name = (const uint8_t *)"Periodic ADV";
|
||||
adv_fields.name_len = strlen((char *)adv_fields.name);
|
||||
|
||||
/* Default to legacy PDUs size, mbuf chain will be increased if needed
|
||||
*/
|
||||
data = os_msys_get_pkthdr(BLE_HCI_MAX_ADV_DATA_LEN, 0);
|
||||
assert(data);
|
||||
|
||||
rc = ble_hs_adv_set_fields_mbuf(&adv_fields, data);
|
||||
assert(rc == 0);
|
||||
|
||||
rc = ble_gap_ext_adv_set_data(instance, data);
|
||||
assert(rc == 0);
|
||||
|
||||
/* configure periodic advertising */
|
||||
memset(&pparams, 0, sizeof(pparams));
|
||||
pparams.include_tx_power = 0;
|
||||
pparams.itvl_min = BLE_GAP_ADV_ITVL_MS(120);
|
||||
pparams.itvl_max = BLE_GAP_ADV_ITVL_MS(240);
|
||||
|
||||
rc = ble_gap_periodic_adv_configure(instance, &pparams);
|
||||
assert(rc == 0);
|
||||
|
||||
data = os_msys_get_pkthdr(sizeof(periodic_adv_raw_data), 0);
|
||||
assert(data);
|
||||
|
||||
rc = os_mbuf_append(data, periodic_adv_raw_data, sizeof(periodic_adv_raw_data));
|
||||
assert(rc == 0);
|
||||
rc = ble_gap_periodic_adv_set_data(instance, data);
|
||||
assert (rc == 0);
|
||||
|
||||
/* start periodic advertising */
|
||||
assert (rc == 0 rc = ble_gap_periodic_adv_start(instance);
|
||||
);
|
||||
|
||||
/* start advertising */
|
||||
rc = ble_gap_ext_adv_start(instance, 0, 0);
|
||||
assert (rc == 0);
|
||||
|
||||
MODLOG_DFLT(INFO, "instance %u started (periodic)\n", instance);
|
||||
}
|
||||
```
|
||||
|
||||
The periodic advertisement uses a non-connectable advertising mode. `memset (¶ms, 0, sizeof(params))` initialises params to 0. This also sets `params.connectable` to 0.
|
||||
|
||||
## Parameter Configuration
|
||||
|
||||
The below snippets represent the parameter configuration for extended and periodic advertisement.
|
||||
|
||||
### For Extended Advertisement
|
||||
|
||||
```c
|
||||
params.own_addr_type = BLE_OWN_ADDR_RANDOM; //Own address type is set to Random
|
||||
params.primary_phy = BLE_HCI_LE_PHY_1M; // Primary advertising PHY is set to 1M
|
||||
params.secondary_phy = BLE_HCI_LE_PHY_2M; // Secondary advertising PHY is set to 2M
|
||||
params.sid = 2; // Advertising set Id is assigned with value 2.
|
||||
```
|
||||
|
||||
### For Periodic Advertisement
|
||||
|
||||
```c
|
||||
memset(&pparams, 0, sizeof(pparams));
|
||||
pparams.include_tx_power = 0; // Indicates that TX power is not included in advertising PDU
|
||||
pparams.itvl_min = BLE_GAP_ADV_ITVL_MS(120); // Minimum advertising interval of 240ms
|
||||
pparams.itvl_max = BLE_GAP_ADV_ITVL_MS(240); //Maximum advertising interval of 480ms
|
||||
```
|
||||
|
||||
Periodic advertisement is started for a particular advertisement instance by calling the API `ble_gap_periodic_adv_start(instance)`. This function takes instance-id as an input parameter. It defines the hci command by initializing the command parameters which are represented in the following lines.
|
||||
|
||||
```c
|
||||
struct ble_hci_le_set_periodic_adv_enable_cp cmd;
|
||||
cmd.enable = 0x01;
|
||||
cmd.adv_handle = instance;
|
||||
```
|
||||
|
||||
Extended advertising is invoked for a particular instance using the API call `ble_gap_ext_adv_start(instance, 0, 0)`.Instance-id, duration, and max_events are input parameters for this API call respectively.
|
||||
|
||||
Duration represents the time for which the adverteiment will take place. Upon expiration, the advertising procedure ends, and the BLE_GAP_EVENT_ADV_COMPLETE event is reported.0 value is used for no expiration.
|
||||
|
||||
max_events Number of advertising events that should be sent before advertising ends and a BLE_GAP_EVENT_ADV_COMPLETE event is reported.0 value is used for no limit.
|
||||
|
||||
## Conclusion
|
||||
|
||||
This Walkthrough covers the code explanation of the BLE_PERIODIC_ADV. The following points are concluded through this walkthrough.
|
||||
|
||||
1. Periodic advertising allows the scanner to sync with the advertiser so the scanner and advertiser would wake up at the same time.
|
||||
2. Periodic advertisement uses NRPA (Non Resolvable private address). It is a randomly generated address that changes periodically to prevent long-term tracking of a device.
|
||||
3. Extended advertising is used to indicate to the scanner that the advertiser is utilizing periodic advertising. Therefore, periodic advertising is started before extended advertising so that the scanner and advertiser can synchronize their actions and operate at the same time.
|
@ -0,0 +1,6 @@
|
||||
# The following lines of boilerplate have to be in your project's
|
||||
# CMakeLists in this exact order for cmake to work correctly
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(ble_pawr_sync_conn)
|
@ -0,0 +1,75 @@
|
||||
| Supported Targets | ESP32-C6 |
|
||||
| ----------------- | -------- |
|
||||
|
||||
# BLE Periodic Advertiser Example
|
||||
|
||||
(See the README.md file in the upper level 'examples' directory for more information about examples.)
|
||||
|
||||
This example starts periodic advertising with non resolvable private address.
|
||||
|
||||
It uses Bluetooth controller and NimBLE stack based BLE host.
|
||||
|
||||
This example aims at understanding periodic advertisement and related NimBLE APIs.
|
||||
|
||||
|
||||
To test this demo, any BLE Periodic Sync app can be used.
|
||||
|
||||
|
||||
Note :
|
||||
|
||||
* Make sure to run `python -m pip install --user -r $IDF_PATH/requirements.txt -r $IDF_PATH/tools/ble/requirements.txt` to install the dependency packages needed.
|
||||
* Currently this Python utility is only supported on Linux (BLE communication is via BLuez + DBus).
|
||||
|
||||
## How to Use Example
|
||||
|
||||
Before project configuration and build, be sure to set the correct chip target using:
|
||||
|
||||
```bash
|
||||
idf.py set-target <chip_name>
|
||||
```
|
||||
|
||||
### Configure the project
|
||||
|
||||
Open the project configuration menu:
|
||||
|
||||
```bash
|
||||
idf.py menuconfig
|
||||
```
|
||||
|
||||
In the `Example Configuration` menu:
|
||||
|
||||
* Select I/O capabilities of device from `Example Configuration --> I/O Capability`, default is `Just_works`.
|
||||
|
||||
### Build and Flash
|
||||
|
||||
Run `idf.py -p PORT flash monitor` to build, flash and monitor the project.
|
||||
|
||||
(To exit the serial monitor, type ``Ctrl-]``.)
|
||||
|
||||
See the [Getting Started Guide](https://idf.espressif.com/) for full steps to configure and use ESP-IDF to build projects.
|
||||
|
||||
## Example Output
|
||||
|
||||
There is this console output when periodic_adv is started:
|
||||
|
||||
```
|
||||
I (313) BTDM_INIT: BT controller compile version [2ee0168]
|
||||
I (313) phy_init: phy_version 912,d001756,Jun 2 2022,16:28:07
|
||||
I (353) system_api: Base MAC address is not set
|
||||
I (353) system_api: read default base MAC address from EFUSE
|
||||
I (353) BTDM_INIT: Bluetooth MAC: 84:f7:03:08:14:8e
|
||||
|
||||
I (363) NimBLE_BLE_PERIODIC_ADV: BLE Host Task Started
|
||||
I (373) NimBLE: Device Address:
|
||||
I (373) NimBLE: d0:42:3a:95:84:05
|
||||
I (373) NimBLE:
|
||||
|
||||
I (383) NimBLE: instance 1 started (periodic)
|
||||
```
|
||||
|
||||
## Note
|
||||
* Periodic sync transfer is not implemented for now.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
For any technical queries, please open an [issue](https://github.com/espressif/esp-idf/issues) on GitHub. We will get back to you soon.
|
@ -0,0 +1,4 @@
|
||||
set(srcs "main.c")
|
||||
|
||||
idf_component_register(SRCS "${srcs}"
|
||||
INCLUDE_DIRS ".")
|
@ -0,0 +1,27 @@
|
||||
menu "Example Configuration"
|
||||
|
||||
config EXAMPLE_EXTENDED_ADV
|
||||
bool
|
||||
depends on SOC_BLE_50_SUPPORTED
|
||||
default y if SOC_ESP_NIMBLE_CONTROLLER
|
||||
select BT_NIMBLE_EXT_ADV
|
||||
prompt "Enable Extended Adv"
|
||||
help
|
||||
Use this option to enable extended advertising in the example.
|
||||
If you disable this option, ensure config BT_NIMBLE_EXT_ADV is
|
||||
also disabled from Nimble stack menuconfig.
|
||||
|
||||
config EXAMPLE_RANDOM_ADDR
|
||||
bool
|
||||
prompt "Advertise RANDOM Address"
|
||||
help
|
||||
Use this option to advertise a random address instead of public address
|
||||
|
||||
config EXAMPLE_PERIODIC_ADV_ENH
|
||||
bool
|
||||
prompt "Enable Periodic Adv Enhancements"
|
||||
depends on SOC_BLE_50_SUPPORTED && SOC_BLE_PERIODIC_ADV_ENH_SUPPORTED
|
||||
select BT_NIMBLE_PERIODIC_ADV_ENH
|
||||
help
|
||||
Use this option to enable periodic advertising enhancements
|
||||
endmenu
|
@ -0,0 +1,3 @@
|
||||
dependencies:
|
||||
nimble_peripheral_utils:
|
||||
path: ${IDF_PATH}/examples/bluetooth/nimble/common/nimble_peripheral_utils
|
@ -0,0 +1,292 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
#include "esp_log.h"
|
||||
#include "nvs_flash.h"
|
||||
/* BLE */
|
||||
#include "nimble/nimble_port.h"
|
||||
#include "nimble/nimble_port_freertos.h"
|
||||
#include "host/ble_hs.h"
|
||||
#include "host/util/util.h"
|
||||
|
||||
#define TAG "NimBLE_BLE_PAwR"
|
||||
#define TARGET_NAME "Nimble_PAwR"
|
||||
#define BLE_PAWR_RSP_DATA_LEN (20)
|
||||
static uint8_t sub_data_pattern[BLE_PAWR_RSP_DATA_LEN] = {0};
|
||||
|
||||
static int create_periodic_sync(struct ble_gap_ext_disc_desc *disc);
|
||||
static void start_scan(void);
|
||||
|
||||
static struct ble_hs_adv_fields fields;
|
||||
static bool synced = false;
|
||||
uint8_t device_addr[6];
|
||||
|
||||
static struct ble_gap_conn_desc desc;
|
||||
char *
|
||||
addr_str(const void *addr)
|
||||
{
|
||||
static char buf[6 * 2 + 5 + 1];
|
||||
const uint8_t *u8p;
|
||||
|
||||
u8p = addr;
|
||||
sprintf(buf, "%02x:%02x:%02x:%02x:%02x:%02x",
|
||||
u8p[5], u8p[4], u8p[3], u8p[2], u8p[1], u8p[0]);
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
void
|
||||
print_conn_desc(const struct ble_gap_conn_desc *desc)
|
||||
{
|
||||
ESP_LOGI(TAG,"handle=%d our_ota_addr_type=%d our_ota_addr=%s ",
|
||||
desc->conn_handle, desc->our_ota_addr.type,
|
||||
addr_str(desc->our_ota_addr.val));
|
||||
ESP_LOGI(TAG, "our_id_addr_type=%d our_id_addr=%s ",
|
||||
desc->our_id_addr.type, addr_str(desc->our_id_addr.val));
|
||||
ESP_LOGI(TAG, "peer_ota_addr_type=%d peer_ota_addr=%s ",
|
||||
desc->peer_ota_addr.type, addr_str(desc->peer_ota_addr.val));
|
||||
ESP_LOGI(TAG, "peer_id_addr_type=%d peer_id_addr=%s ",
|
||||
desc->peer_id_addr.type, addr_str(desc->peer_id_addr.val));
|
||||
ESP_LOGI(TAG, "conn_itvl=%d conn_latency=%d supervision_timeout=%d "
|
||||
"encrypted=%d authenticated=%d bonded=%d\n",
|
||||
desc->conn_itvl, desc->conn_latency,
|
||||
desc->supervision_timeout,
|
||||
desc->sec_state.encrypted,
|
||||
desc->sec_state.authenticated,
|
||||
desc->sec_state.bonded);
|
||||
}
|
||||
static int
|
||||
gap_event_cb(struct ble_gap_event *event, void *arg)
|
||||
{
|
||||
int rc;
|
||||
uint8_t *addr;
|
||||
struct ble_gap_ext_disc_desc *disc;
|
||||
|
||||
switch (event->type) {
|
||||
case BLE_GAP_EVENT_CONNECT:
|
||||
ESP_LOGI(TAG, "Connection established, conn_handle = 0x%0x, sync handle= 0x%02x, status = 0x%0x\n",event->connect.conn_handle, event->connect.sync_handle, event->connect.status);
|
||||
rc = ble_gap_conn_find(event->connect.conn_handle, &desc);
|
||||
if(rc == 0){
|
||||
print_conn_desc(&desc);
|
||||
}
|
||||
else{
|
||||
ESP_LOGE(TAG,"Failed to find Conn Information");
|
||||
}
|
||||
return 0;
|
||||
case BLE_GAP_EVENT_EXT_DISC:
|
||||
disc = &event->ext_disc;
|
||||
addr = disc->addr.val;
|
||||
ESP_LOGI(TAG, "[Disc advertiser] addr %02x:%02x:%02x:%02x:%02x:%02x, props: 0x%x, rssi:%d",
|
||||
addr[5], addr[4], addr[3], addr[2], addr[1], addr[0], disc->props, disc->rssi);
|
||||
if (synced) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
rc = ble_hs_adv_parse_fields(&fields, disc->data, disc->length_data);
|
||||
if (rc != 0) {
|
||||
ESP_LOGE(TAG, "Failed to parse adv data, rc = %d", rc);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (disc->periodic_adv_itvl && fields.name_len && !memcmp(fields.name, TARGET_NAME, strlen(TARGET_NAME))) {
|
||||
create_periodic_sync(disc);
|
||||
}
|
||||
return 0;
|
||||
|
||||
case BLE_GAP_EVENT_PERIODIC_REPORT:
|
||||
if (event->periodic_report.event_counter % 10 == 0) {
|
||||
// print every 10th event
|
||||
ESP_LOGI(TAG, "[Periodic Adv Report] handle:%d, rssi:%d, data status:0x%x",
|
||||
event->periodic_report.sync_handle, event->periodic_report.rssi,
|
||||
event->periodic_report.data_status);
|
||||
ESP_LOGI(TAG, "[Periodic Adv Report] event_counter(%d), subevent(%d)",
|
||||
event->periodic_report.event_counter, event->periodic_report.subevent);
|
||||
}
|
||||
|
||||
struct ble_gap_periodic_adv_response_params param = {
|
||||
.request_event = event->periodic_report.event_counter,
|
||||
.request_subevent = event->periodic_report.subevent,
|
||||
.response_subevent = event->periodic_report.subevent,
|
||||
.response_slot = 0
|
||||
};
|
||||
|
||||
struct os_mbuf *data = os_msys_get_pkthdr(BLE_PAWR_RSP_DATA_LEN, 0);
|
||||
if (!data) {
|
||||
ESP_LOGE(TAG, "No memory");
|
||||
return 0;
|
||||
}
|
||||
// create a special data for checking manually in ADV side
|
||||
sub_data_pattern[0] = event->periodic_report.data[0];
|
||||
rc = ble_hs_id_copy_addr(BLE_ADDR_PUBLIC, device_addr, NULL);
|
||||
memset(sub_data_pattern + 1, event->periodic_report.subevent, BLE_PAWR_RSP_DATA_LEN - 1);
|
||||
memcpy(sub_data_pattern + 1,device_addr,BLE_DEV_ADDR_LEN);
|
||||
os_mbuf_append(data, sub_data_pattern, BLE_PAWR_RSP_DATA_LEN);
|
||||
|
||||
rc = ble_gap_periodic_adv_set_response_data(event->periodic_report.sync_handle, ¶m, data);
|
||||
if (rc) {
|
||||
ESP_LOGE(TAG, "Set response data failed, subev(%x), rsp_slot(%d), rc(0x%x)",
|
||||
sub_data_pattern[0], event->periodic_report.subevent, rc);
|
||||
}
|
||||
os_mbuf_free_chain(data);
|
||||
|
||||
return 0;
|
||||
|
||||
case BLE_GAP_EVENT_PERIODIC_SYNC_LOST:
|
||||
ESP_LOGE(TAG, "[Periodic Sync Lost] handle:%d, Reason = 0x%x",
|
||||
event->periodic_sync_lost.sync_handle, event->periodic_sync_lost.reason);
|
||||
synced = false;
|
||||
start_scan();
|
||||
return 0;
|
||||
|
||||
case BLE_GAP_EVENT_PERIODIC_SYNC:
|
||||
if (!event->periodic_sync.status) {
|
||||
ESP_LOGI(TAG, "[Periodic Sync Established] sync handle:%d, num_subevents:0x%x",
|
||||
event->periodic_sync.sync_handle, event->periodic_sync.num_subevents);
|
||||
ESP_LOGI(TAG, "subevent_interval:0x%x, slot_delay:0x%x,slot_spacing:0x%x",
|
||||
event->periodic_sync.subevent_interval,
|
||||
event->periodic_sync.response_slot_delay,
|
||||
event->periodic_sync.response_slot_spacing);
|
||||
ble_gap_disc_cancel();
|
||||
|
||||
// choose subevents in range 0 to (num_subevents - 1)
|
||||
uint8_t subevents[] = {0, 1, 2, 3, 4};
|
||||
int result = ble_gap_periodic_adv_sync_subev(event->periodic_sync.sync_handle, 0, sizeof(subevents), subevents);
|
||||
if (result == ESP_OK) {
|
||||
ESP_LOGI(TAG, "[Subevent Sync OK] sync handle:%d, sync_subevents:%d\n", event->periodic_sync.sync_handle, sizeof(subevents));
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Failed to sync subevents, rc = 0x%x", result);
|
||||
}
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Periodic Sync Error, status = %d", event->periodic_sync.status);
|
||||
synced = false;
|
||||
start_scan();
|
||||
}
|
||||
return 0;
|
||||
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
create_periodic_sync(struct ble_gap_ext_disc_desc *disc)
|
||||
{
|
||||
int rc;
|
||||
struct ble_gap_periodic_sync_params params;
|
||||
|
||||
params.skip = 0;
|
||||
params.sync_timeout = 4000;
|
||||
params.reports_disabled = 0;
|
||||
|
||||
#if CONFIG_EXAMPLE_PERIODIC_ADV_ENH
|
||||
/* This way the periodic advertising reports will not be
|
||||
delivered to host unless the advertising data is changed
|
||||
or the Data-Id is updated by the advertiser */
|
||||
params.filter_duplicates = 1;
|
||||
#endif
|
||||
|
||||
rc = ble_gap_periodic_adv_sync_create(&disc->addr, disc->sid, ¶ms, gap_event_cb, NULL);
|
||||
if (!rc) {
|
||||
synced = true;
|
||||
ESP_LOGI(TAG, "Create sync");
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Failed to create sync, rc = %d", rc);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void
|
||||
start_scan(void)
|
||||
{
|
||||
int rc;
|
||||
struct ble_gap_ext_disc_params disc_params;
|
||||
|
||||
/* Perform a passive scan. I.e., don't send follow-up scan requests to
|
||||
* each advertiser.
|
||||
*/
|
||||
disc_params.itvl = BLE_GAP_SCAN_ITVL_MS(600);
|
||||
disc_params.window = BLE_GAP_SCAN_ITVL_MS(300);
|
||||
disc_params.passive = 1;
|
||||
|
||||
/* Tell the controller to filter duplicates; we don't want to process
|
||||
* repeated advertisements from the same device.
|
||||
*/
|
||||
rc = ble_gap_ext_disc(BLE_OWN_ADDR_PUBLIC, 0, 0, 1, 0, 0, NULL, &disc_params,
|
||||
gap_event_cb, NULL);
|
||||
if (rc != 0) {
|
||||
ESP_LOGE(TAG, "Error initiating GAP discovery procedure; rc=%d\n", rc);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
on_reset(int reason)
|
||||
{
|
||||
ESP_LOGE(TAG, "Resetting state; reason=%d\n", reason);
|
||||
}
|
||||
|
||||
/* Cnnot find `ble_single_xxxx()`, workaround */
|
||||
// static void
|
||||
// on_sync(void)
|
||||
// {
|
||||
// int ble_single_env_init(void);
|
||||
// int ble_single_init(void);
|
||||
|
||||
// int rc;
|
||||
|
||||
// rc = ble_single_env_init();
|
||||
// assert(!rc);
|
||||
// rc = ble_single_init();
|
||||
// assert(!rc);
|
||||
|
||||
// start_scan();
|
||||
// }
|
||||
|
||||
static void
|
||||
on_sync(void)
|
||||
{
|
||||
int rc;
|
||||
|
||||
/* Make sure we have proper identity address set (public preferred) */
|
||||
rc = ble_hs_util_ensure_addr(0);
|
||||
assert(rc == 0);
|
||||
|
||||
start_scan();
|
||||
}
|
||||
|
||||
void pawr_host_task(void *param)
|
||||
{
|
||||
ESP_LOGI(TAG, "BLE Host Task Started");
|
||||
/* This function will return only when nimble_port_stop() is executed */
|
||||
nimble_port_run();
|
||||
|
||||
nimble_port_freertos_deinit();
|
||||
}
|
||||
|
||||
void
|
||||
app_main(void)
|
||||
{
|
||||
esp_err_t ret;
|
||||
|
||||
/* Initialize NVS — it is used to store PHY calibration data */
|
||||
ret = nvs_flash_init();
|
||||
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
|
||||
ESP_ERROR_CHECK(nvs_flash_erase());
|
||||
ret = nvs_flash_init();
|
||||
}
|
||||
ESP_ERROR_CHECK(ret);
|
||||
|
||||
ret = nimble_port_init();
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to init nimble %d ", ret);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Initialize the NimBLE host configuration. */
|
||||
ble_hs_cfg.reset_cb = on_reset;
|
||||
ble_hs_cfg.sync_cb = on_sync;
|
||||
|
||||
nimble_port_freertos_init(pawr_host_task);
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
# Override some defaults so BT stack is enabled
|
||||
# in this example
|
||||
|
||||
#
|
||||
# BT config
|
||||
#
|
||||
CONFIG_BT_ENABLED=y
|
||||
CONFIG_BT_BLUEDROID_ENABLED=n
|
||||
CONFIG_BT_NIMBLE_ENABLED=y
|
||||
CONFIG_BT_NIMBLE_EXT_ADV=n
|
||||
CONFIG_BT_NIMBLE_PERIODIC_ADV_WITH_RESPONSES=y
|
||||
CONFIG_BT_NIMBLE_MAX_PERIODIC_SYNCS=1
|
||||
CONFIG_BT_NIMBLE_ROLE_CENTRAL=n
|
||||
CONFIG_BT_NIMBLE_ROLE_OBSERVER=y
|
||||
|
||||
CONFIG_BT_CONTROLLER_DISABLED=y
|
||||
|
||||
#
|
||||
# Host-controller Transport
|
||||
#
|
||||
CONFIG_BT_NIMBLE_TRANSPORT_UART_PORT=1
|
||||
CONFIG_UART_BAUDRATE_115200=y
|
||||
CONFIG_BT_NIMBLE_UART_TX_PIN=20
|
||||
CONFIG_BT_NIMBLE_UART_RX_PIN=21
|
||||
CONFIG_UART_HW_FLOWCTRL_CTS_RTS=n
|
||||
# CONFIG_BT_NIMBLE_HCI_UART_RTS_PIN=22
|
||||
# CONFIG_BT_NIMBLE_HCI_UART_CTS_PIN=23
|
||||
# end of Host-controller Transport
|
||||
# end of NimBLE Options
|
||||
|
||||
# C6 Nordic
|
||||
# TX: 20 ---- RX
|
||||
# RX: 21 ---- TX
|
||||
# RTS: 22 ---- CTS
|
||||
# CTS: 23 ---- RTS
|
@ -0,0 +1,320 @@
|
||||
# BLE Periodic Advertisement Example Walkthrough
|
||||
|
||||
## Introduction
|
||||
|
||||
In this tutorial, the ble_periodic_adv example code for the espressif chipsets with BLE5.0 support is reviewed. This example aims at understanding periodic advertisements and related NimBLE APIs.This code implements the periodic advertisement functionality along with extended advertisement by generating a non-resolvable private address.
|
||||
|
||||
## Includes
|
||||
|
||||
This example is located in the examples folder of the ESP-IDF under the [ble_periodic_adv/main](../main). The [main.c](../main/main.c) file located in the main folder contains all the functionality that we are going to review. The header files contained in [main.c](../main/main.c) are:
|
||||
|
||||
```c
|
||||
#include "esp_log.h"
|
||||
#include "nvs_flash.h"
|
||||
/* BLE */
|
||||
#include "nimble/nimble_port.h"
|
||||
#include "nimble/nimble_port_freertos.h"
|
||||
#include "host/ble_hs.h"
|
||||
#include "host/util/util.h"
|
||||
#include "console/console.h"
|
||||
#include "services/gap/ble_svc_gap.h"
|
||||
#include "periodic_adv.h"
|
||||
#include "host/ble_gap.h"
|
||||
#include "host/ble_hs_adv.h"
|
||||
#include "patterns.h"
|
||||
```
|
||||
These `includes` are required for the FreeRTOS and underlying system components to run, including the logging functionality and a library to store data in non-volatile flash memory. We are interested in `“nimble_port.h”`, `“nimble_port_freertos.h”`, `"ble_hs.h"` and `“ble_svc_gap.h”`, `“periodic_adv.h”` which expose the BLE APIs required to implement this example.
|
||||
|
||||
* `nimble_port.h`: Includes the declaration of functions required for the initialization of the nimble stack.
|
||||
* `nimble_port_freertos.h`: Initializes and enables nimble host task.
|
||||
* `ble_hs.h`: Defines the functionalities to handle the host event
|
||||
* `ble_svc_gap.h`:Defines the macros for device name, and device appearance and declares the function to set them.
|
||||
* `periodic_adv.h`:It includes the code containing forward declarations of 2 structs `ble_hs_cfg` , and `ble_gatt_register_ctxt` based on weather macro `H_BLE_PERIODIC_ADV_` is defined.
|
||||
|
||||
## Main Entry Point
|
||||
|
||||
The program’s entry point is the app_main() function:
|
||||
```c
|
||||
void
|
||||
app_main(void)
|
||||
{
|
||||
int rc;
|
||||
/* Initialize NVS — it is used to store PHY calibration data */
|
||||
esp_err_t ret = nvs_flash_init();
|
||||
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
|
||||
ESP_ERROR_CHECK(nvs_flash_erase());
|
||||
ret = nvs_flash_init();
|
||||
}
|
||||
ESP_ERROR_CHECK(ret);
|
||||
|
||||
ret = nimble_port_init();
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(tag, "Failed to init nimble %d ", ret);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Initialize the NimBLE host configuration. */
|
||||
ble_hs_cfg.reset_cb = periodic_adv_on_reset;
|
||||
ble_hs_cfg.sync_cb = periodic_adv_on_sync;
|
||||
ble_hs_cfg.store_status_cb = ble_store_util_status_rr;
|
||||
/* Set the default device name. */
|
||||
rc = ble_svc_gap_device_name_set("nimble_periodic_adv");
|
||||
assert(rc == 0);
|
||||
|
||||
/* XXX Need to have a template for store */
|
||||
ble_store_config_init();
|
||||
|
||||
nimble_port_freertos_init(periodic_adv_host_task);
|
||||
}
|
||||
```
|
||||
The main function starts by initializing the non-volatile storage library. This library allows us to save the key-value pairs in flash memory.`nvs_flash_init()` stores the PHY calibration data. In a Bluetooth Low Energy (BLE) device, cryptographic keys used for encryption and authentication are often stored in Non-Volatile Storage (NVS).BLE stores the peer keys, CCCD keys, peer records, etc on NVS. By storing these keys in NVS, the BLE device can quickly retrieve them when needed, without the need for time-consuming key generations.
|
||||
```c
|
||||
esp_err_t ret = nvs_flash_init();
|
||||
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
|
||||
ESP_ERROR_CHECK(nvs_flash_erase());
|
||||
ret = nvs_flash_init();
|
||||
}
|
||||
ESP_ERROR_CHECK( ret );
|
||||
```
|
||||
|
||||
## BT Controller and Stack Initialization
|
||||
|
||||
The main function calls `nimble_port_init()` to initialize BT Controller and nimble stack. This function initializes the BT controller by first creating its configuration structure named `esp_bt_controller_config_t` with default settings generated by the `BT_CONTROLLER_INIT_CONFIG_DEFAULT()` macro. It implements the Host Controller Interface (HCI) on the controller side, the Link Layer (LL), and the Physical Layer (PHY). The BT Controller is invisible to the user applications and deals with the lower layers of the BLE stack. The controller configuration includes setting the BT controller stack size, priority, and HCI baud rate. With the settings created, the BT controller is initialized and enabled with the `esp_bt_controller_init()` and `esp_bt_controller_enable()` functions:
|
||||
|
||||
```c
|
||||
esp_bt_controller_config_t config_opts = BT_CONTROLLER_INIT_CONFIG_DEFAULT();
|
||||
ret = esp_bt_controller_init(&config_opts);
|
||||
```
|
||||
Next, the controller is enabled in BLE Mode.
|
||||
|
||||
```c
|
||||
ret = esp_bt_controller_enable(ESP_BT_MODE_BLE);
|
||||
```
|
||||
The controller should be enabled in `ESP_BT_MODE_BLE` if you want to use the BLE mode.
|
||||
|
||||
There are four Bluetooth modes supported:
|
||||
|
||||
1. `ESP_BT_MODE_IDLE`: Bluetooth not running
|
||||
2. `ESP_BT_MODE_BLE`: BLE mode
|
||||
3. `ESP_BT_MODE_CLASSIC_BT`: BT Classic mode
|
||||
4. `ESP_BT_MODE_BTDM`: Dual mode (BLE + BT Classic)
|
||||
|
||||
After the initialization of the BT controller, the nimble stack, which includes the common definitions and APIs for BLE, is initialized by using `esp_nimble_init()`:
|
||||
|
||||
```c
|
||||
esp_err_t esp_nimble_init(void)
|
||||
{
|
||||
|
||||
#if !SOC_ESP_NIMBLE_CONTROLLER
|
||||
/* Initialize the function pointers for OS porting */
|
||||
npl_freertos_funcs_init();
|
||||
|
||||
npl_freertos_mempool_init();
|
||||
|
||||
if(esp_nimble_hci_init() != ESP_OK) {
|
||||
ESP_LOGE(NIMBLE_PORT_LOG_TAG, "hci inits failed\n");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
/* Initialize default event queue */
|
||||
ble_npl_eventq_init(&g_eventq_dflt);
|
||||
|
||||
os_msys_init();
|
||||
|
||||
void ble_store_ram_init(void);
|
||||
/* XXX Need to have a template for store */
|
||||
ble_store_ram_init();
|
||||
#endif
|
||||
|
||||
/* Initialize the host */
|
||||
ble_hs_init();
|
||||
return ESP_OK;
|
||||
}
|
||||
```
|
||||
|
||||
The host is configured by setting up the callbacks for Stack-reset, Stack-sync, and Storage status
|
||||
|
||||
```c
|
||||
ble_hs_cfg.reset_cb = periodic_adv_on_reset;
|
||||
ble_hs_cfg.sync_cb = periodic_adv_on_sync;
|
||||
ble_hs_cfg.store_status_cb = ble_store_util_status_rr;
|
||||
```
|
||||
|
||||
The main function calls `ble_svc_gap_device_name_set()` to set the default device name. 'blecent_phy' is passed as the default device name to this function.
|
||||
```c
|
||||
rc = ble_svc_gap_device_name_set("nimble_periodic_adv");
|
||||
```
|
||||
main function calls `ble_store_config_init()` to configure the host by setting up the storage callbacks which handle the read, write, and deletion of security material.
|
||||
```c
|
||||
/* XXX Need to have a template for store */
|
||||
ble_store_config_init();
|
||||
```
|
||||
|
||||
The main function ends by creating a task where nimble will run using `nimble_port_freertos_init()`. This enables the nimble stack by using `esp_nimble_enable()`.
|
||||
|
||||
```c
|
||||
nimble_port_freertos_init(periodic_adv_host_task);
|
||||
```
|
||||
`esp_nimble_enable()` create a task where the nimble host will run. It is not strictly necessary to have a separate task for the nimble host, but to handle the default queue, it is easier to create a separate task.
|
||||
|
||||
## Generation of non-resolvable private address
|
||||
|
||||
In Bluetooth Low Energy (BLE), a non-resolvable private address is a type of Bluetooth device address that is used for privacy purposes. It is a randomly generated address that changes periodically to prevent long-term tracking of a device. The API call to `ble_hs_id_gen_rnd()` is responsible for generating a non-resolvable private address. NRPA is a 48-bit address that is stored in `addr.val`.
|
||||
|
||||
```c
|
||||
#if CONFIG_EXAMPLE_RANDOM_ADDR
|
||||
static void
|
||||
periodic_adv_set_addr(void)
|
||||
{
|
||||
ble_addr_t addr;
|
||||
int rc;
|
||||
|
||||
/* generate new non-resolvable private address */
|
||||
rc = ble_hs_id_gen_rnd(0, &addr);
|
||||
assert(rc == 0);
|
||||
|
||||
/* set generated address */
|
||||
rc = ble_hs_id_set_rnd(addr.val);
|
||||
|
||||
assert(rc == 0);
|
||||
}
|
||||
#endif
|
||||
```
|
||||
|
||||
## Periodic Advertisement
|
||||
|
||||
Periodic advertisement start by creating instances of structures `ble_gap_periodic_adv_params`, `ble_gap_ext_adv_params`, `ble_hs_adv_fields`, and `os_mbuf`. Advertising parameters such as connecting modes, advertising intervals, peer address, advertising-filter policy, etc are defined in these structures for periodic and extended advertisements. `pparams` and `params` instances have parameters for periodic advertisement and extended advertisement respectively.
|
||||
Bluetooth device address is given by the structure ble_aadr_t which contains the fields for address type and address value.
|
||||
|
||||
## Need of Extended Advertisement in Periodic Advertisement
|
||||
|
||||
Non-connectable and non-scannable advertising events containing synchronization information about a periodic advertising train are necessary for the scanner device to sync with the periodic advertising train. The periodic advertising will utilize the same physical layer (PHY) as the auxiliary packet, which is part of the extended advertisement.
|
||||
|
||||
|
||||
Below is the implementation to start periodic advertisement.
|
||||
|
||||
```c
|
||||
static void
|
||||
start_periodic_adv(void)
|
||||
{
|
||||
int rc;
|
||||
struct ble_gap_periodic_adv_params pparams;
|
||||
struct ble_gap_ext_adv_params params;
|
||||
struct ble_hs_adv_fields adv_fields;
|
||||
struct os_mbuf *data;
|
||||
uint8_t instance = 1;
|
||||
ble_addr_t addr;
|
||||
|
||||
/* set random (NRPA) address for instance */
|
||||
rc = ble_hs_id_gen_rnd(1, &addr);
|
||||
assert (rc == 0);
|
||||
|
||||
MODLOG_DFLT(INFO, "Device Address: ");
|
||||
print_addr(addr.val);
|
||||
MODLOG_DFLT(INFO, "\n");
|
||||
|
||||
/* For periodic we use instance with non-connectable advertising */
|
||||
memset (¶ms, 0, sizeof(params));
|
||||
|
||||
/* advertise using random addr */
|
||||
params.own_addr_type = BLE_OWN_ADDR_RANDOM;
|
||||
params.primary_phy = BLE_HCI_LE_PHY_1M;
|
||||
params.secondary_phy = BLE_HCI_LE_PHY_2M;
|
||||
params.sid = 2;
|
||||
|
||||
/* configure instance 1 */
|
||||
rc = ble_gap_ext_adv_configure(instance, ¶ms, NULL, NULL, NULL);
|
||||
assert (rc == 0);
|
||||
|
||||
rc = ble_gap_ext_adv_set_addr(instance, &addr );
|
||||
assert (rc == 0);
|
||||
|
||||
memset(&adv_fields, 0, sizeof(adv_fields));
|
||||
adv_fields.name = (const uint8_t *)"Periodic ADV";
|
||||
adv_fields.name_len = strlen((char *)adv_fields.name);
|
||||
|
||||
/* Default to legacy PDUs size, mbuf chain will be increased if needed
|
||||
*/
|
||||
data = os_msys_get_pkthdr(BLE_HCI_MAX_ADV_DATA_LEN, 0);
|
||||
assert(data);
|
||||
|
||||
rc = ble_hs_adv_set_fields_mbuf(&adv_fields, data);
|
||||
assert(rc == 0);
|
||||
|
||||
rc = ble_gap_ext_adv_set_data(instance, data);
|
||||
assert(rc == 0);
|
||||
|
||||
/* configure periodic advertising */
|
||||
memset(&pparams, 0, sizeof(pparams));
|
||||
pparams.include_tx_power = 0;
|
||||
pparams.itvl_min = BLE_GAP_ADV_ITVL_MS(120);
|
||||
pparams.itvl_max = BLE_GAP_ADV_ITVL_MS(240);
|
||||
|
||||
rc = ble_gap_periodic_adv_configure(instance, &pparams);
|
||||
assert(rc == 0);
|
||||
|
||||
data = os_msys_get_pkthdr(sizeof(periodic_adv_raw_data), 0);
|
||||
assert(data);
|
||||
|
||||
rc = os_mbuf_append(data, periodic_adv_raw_data, sizeof(periodic_adv_raw_data));
|
||||
assert(rc == 0);
|
||||
rc = ble_gap_periodic_adv_set_data(instance, data);
|
||||
assert (rc == 0);
|
||||
|
||||
/* start periodic advertising */
|
||||
assert (rc == 0 rc = ble_gap_periodic_adv_start(instance);
|
||||
);
|
||||
|
||||
/* start advertising */
|
||||
rc = ble_gap_ext_adv_start(instance, 0, 0);
|
||||
assert (rc == 0);
|
||||
|
||||
MODLOG_DFLT(INFO, "instance %u started (periodic)\n", instance);
|
||||
}
|
||||
```
|
||||
|
||||
The periodic advertisement uses a non-connectable advertising mode. `memset (¶ms, 0, sizeof(params))` initialises params to 0. This also sets `params.connectable` to 0.
|
||||
|
||||
## Parameter Configuration
|
||||
|
||||
The below snippets represent the parameter configuration for extended and periodic advertisement.
|
||||
|
||||
### For Extended Advertisement
|
||||
|
||||
```c
|
||||
params.own_addr_type = BLE_OWN_ADDR_RANDOM; //Own address type is set to Random
|
||||
params.primary_phy = BLE_HCI_LE_PHY_1M; // Primary advertising PHY is set to 1M
|
||||
params.secondary_phy = BLE_HCI_LE_PHY_2M; // Secondary advertising PHY is set to 2M
|
||||
params.sid = 2; // Advertising set Id is assigned with value 2.
|
||||
```
|
||||
|
||||
### For Periodic Advertisement
|
||||
|
||||
```c
|
||||
memset(&pparams, 0, sizeof(pparams));
|
||||
pparams.include_tx_power = 0; // Indicates that TX power is not included in advertising PDU
|
||||
pparams.itvl_min = BLE_GAP_ADV_ITVL_MS(120); // Minimum advertising interval of 240ms
|
||||
pparams.itvl_max = BLE_GAP_ADV_ITVL_MS(240); //Maximum advertising interval of 480ms
|
||||
```
|
||||
|
||||
Periodic advertisement is started for a particular advertisement instance by calling the API `ble_gap_periodic_adv_start(instance)`. This function takes instance-id as an input parameter. It defines the hci command by initializing the command parameters which are represented in the following lines.
|
||||
|
||||
```c
|
||||
struct ble_hci_le_set_periodic_adv_enable_cp cmd;
|
||||
cmd.enable = 0x01;
|
||||
cmd.adv_handle = instance;
|
||||
```
|
||||
|
||||
Extended advertising is invoked for a particular instance using the API call `ble_gap_ext_adv_start(instance, 0, 0)`.Instance-id, duration, and max_events are input parameters for this API call respectively.
|
||||
|
||||
Duration represents the time for which the adverteiment will take place. Upon expiration, the advertising procedure ends, and the BLE_GAP_EVENT_ADV_COMPLETE event is reported.0 value is used for no expiration.
|
||||
|
||||
max_events Number of advertising events that should be sent before advertising ends and a BLE_GAP_EVENT_ADV_COMPLETE event is reported.0 value is used for no limit.
|
||||
|
||||
## Conclusion
|
||||
|
||||
This Walkthrough covers the code explanation of the BLE_PERIODIC_ADV. The following points are concluded through this walkthrough.
|
||||
|
||||
1. Periodic advertising allows the scanner to sync with the advertiser so the scanner and advertiser would wake up at the same time.
|
||||
2. Periodic advertisement uses NRPA (Non Resolvable private address). It is a randomly generated address that changes periodically to prevent long-term tracking of a device.
|
||||
3. Extended advertising is used to indicate to the scanner that the advertiser is utilizing periodic advertising. Therefore, periodic advertising is started before extended advertising so that the scanner and advertiser can synchronize their actions and operate at the same time.
|
@ -62,6 +62,11 @@ static const char *tag = "NimBLE_BLE_CENT";
|
||||
static int blecent_gap_event(struct ble_gap_event *event, void *arg);
|
||||
static uint8_t peer_addr[6];
|
||||
|
||||
#if MYNEWT_VAL(BLE_EATT_CHAN_NUM) > 0
|
||||
static uint16_t cids[MYNEWT_VAL(BLE_EATT_CHAN_NUM)];
|
||||
static uint16_t bearers;
|
||||
#endif
|
||||
|
||||
void ble_store_config_init(void);
|
||||
|
||||
/**
|
||||
@ -775,6 +780,14 @@ blecent_gap_event(struct ble_gap_event *event, void *arg)
|
||||
/* Forget about peer. */
|
||||
peer_delete(event->disconnect.conn.conn_handle);
|
||||
|
||||
#if MYNEWT_VAL(BLE_EATT_CHAN_NUM) > 0
|
||||
/* Reset EATT config */
|
||||
bearers = 0;
|
||||
for (int i = 0; i < MYNEWT_VAL(BLE_EATT_CHAN_NUM); i++) {
|
||||
cids[i] = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Resume scanning. */
|
||||
blecent_scan();
|
||||
return 0;
|
||||
@ -791,14 +804,16 @@ blecent_gap_event(struct ble_gap_event *event, void *arg)
|
||||
rc = ble_gap_conn_find(event->enc_change.conn_handle, &desc);
|
||||
assert(rc == 0);
|
||||
print_conn_desc(&desc);
|
||||
#if !MYNEWT_VAL(BLE_EATT_CHAN_NUM)
|
||||
#if CONFIG_EXAMPLE_ENCRYPTION
|
||||
/*** Go for service discovery after encryption has been successfully enabled ***/
|
||||
rc = peer_disc_all(event->connect.conn_handle,
|
||||
rc = peer_disc_all(event->enc_change.conn_handle,
|
||||
blecent_on_disc_complete, NULL);
|
||||
if (rc != 0) {
|
||||
MODLOG_DFLT(ERROR, "Failed to discover services; rc=%d\n", rc);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
return 0;
|
||||
|
||||
@ -870,6 +885,52 @@ blecent_gap_event(struct ble_gap_event *event, void *arg)
|
||||
event->pathloss_threshold.zone_entered);
|
||||
return 0;
|
||||
#endif
|
||||
|
||||
#if MYNEWT_VAL(BLE_EATT_CHAN_NUM) > 0
|
||||
case BLE_GAP_EVENT_EATT:
|
||||
int i;
|
||||
MODLOG_DFLT(INFO, "EATT %s : conn_handle=%d cid=%d",
|
||||
event->eatt.status ? "disconnected" : "connected",
|
||||
event->eatt.conn_handle,
|
||||
event->eatt.cid);
|
||||
if (event->eatt.status) {
|
||||
/* Remove CID from the list of saved CIDs */
|
||||
for (i = 0; i < bearers; i++) {
|
||||
if (cids[i] == event->eatt.cid) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
while (i < (bearers - 1)) {
|
||||
cids[i] = cids[i + 1];
|
||||
i += 1;
|
||||
}
|
||||
cids[i] = 0;
|
||||
|
||||
/* Now Abort */
|
||||
return 0;
|
||||
}
|
||||
cids[bearers] = event->eatt.cid;
|
||||
bearers += 1;
|
||||
if (bearers != MYNEWT_VAL(BLE_EATT_CHAN_NUM)) {
|
||||
/* Wait until all EATT bearers are connected before proceeding */
|
||||
return 0;
|
||||
}
|
||||
/* Set the default bearer to use for further procedures */
|
||||
rc = ble_att_set_default_bearer_using_cid(event->eatt.conn_handle, cids[0]);
|
||||
if (rc != 0) {
|
||||
MODLOG_DFLT(INFO, "Cannot set default EATT bearer, rc = %d\n", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Perform service discovery */
|
||||
rc = peer_disc_all(event->eatt.conn_handle,
|
||||
blecent_on_disc_complete, NULL);
|
||||
if(rc != 0) {
|
||||
MODLOG_DFLT(ERROR, "Failed to discover services; rc=%d\n", rc);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
@ -890,6 +951,7 @@ blecent_on_sync(void)
|
||||
rc = ble_hs_util_ensure_addr(0);
|
||||
assert(rc == 0);
|
||||
|
||||
|
||||
#if !CONFIG_EXAMPLE_INIT_DEINIT_LOOP
|
||||
/* Begin scanning for a peripheral to connect to. */
|
||||
blecent_scan();
|
||||
@ -981,4 +1043,11 @@ app_main(void)
|
||||
stack_init_deinit();
|
||||
#endif
|
||||
|
||||
#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
|
||||
|
||||
}
|
||||
|
@ -45,6 +45,11 @@ static uint8_t own_addr_type = BLE_OWN_ADDR_RANDOM;
|
||||
static uint8_t own_addr_type;
|
||||
#endif
|
||||
|
||||
#if MYNEWT_VAL(BLE_EATT_CHAN_NUM) > 0
|
||||
static uint16_t cids[MYNEWT_VAL(BLE_EATT_CHAN_NUM)];
|
||||
static uint16_t bearers;
|
||||
#endif
|
||||
|
||||
void ble_store_config_init(void);
|
||||
|
||||
/**
|
||||
@ -416,7 +421,7 @@ bleprph_gap_event(struct ble_gap_event *event, void *arg)
|
||||
event->transmit_power.delta);
|
||||
return 0;
|
||||
|
||||
case BLE_GAP_EVENT_PATHLOSS_THRESHOLD:
|
||||
case BLE_GAP_EVENT_PATHLOSS_THRESHOLD:
|
||||
MODLOG_DFLT(INFO, "Pathloss threshold event : conn_handle=%d current path loss=%d "
|
||||
"zone_entered =%d",
|
||||
event->pathloss_threshold.conn_handle,
|
||||
@ -424,6 +429,41 @@ bleprph_gap_event(struct ble_gap_event *event, void *arg)
|
||||
event->pathloss_threshold.zone_entered);
|
||||
return 0;
|
||||
#endif
|
||||
|
||||
#if MYNEWT_VAL(BLE_EATT_CHAN_NUM) > 0
|
||||
case BLE_GAP_EVENT_EATT:
|
||||
MODLOG_DFLT(INFO, "EATT %s : conn_handle=%d cid=%d",
|
||||
event->eatt.status ? "disconnected" : "connected",
|
||||
event->eatt.conn_handle,
|
||||
event->eatt.cid);
|
||||
if (event->eatt.status) {
|
||||
/* Abort if disconnected */
|
||||
return 0;
|
||||
}
|
||||
cids[bearers] = event->eatt.cid;
|
||||
bearers += 1;
|
||||
if (bearers != MYNEWT_VAL(BLE_EATT_CHAN_NUM)) {
|
||||
/* Wait until all EATT bearers are connected before proceeding */
|
||||
return 0;
|
||||
}
|
||||
/* Set the default bearer to use for further procedures */
|
||||
rc = ble_att_set_default_bearer_using_cid(event->eatt.conn_handle, cids[0]);
|
||||
if (rc != 0) {
|
||||
MODLOG_DFLT(INFO, "Cannot set default EATT bearer, rc = %d\n", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
return 0;
|
||||
#endif
|
||||
|
||||
#if MYNEWT_VAL(BLE_CONN_SUBRATING)
|
||||
case BLE_GAP_EVENT_SUBRATE_CHANGE:
|
||||
MODLOG_DFLT(INFO, "Subrate change event : conn_handle=%d status=%d factor=%d",
|
||||
event->subrate_change.conn_handle,
|
||||
event->subrate_change.status,
|
||||
event->subrate_change.subrate_factor);
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -566,4 +606,11 @@ app_main(void)
|
||||
if (rc != ESP_OK) {
|
||||
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
|
||||
}
|
||||
|
Reference in New Issue
Block a user