目錄
概述
1 函數接口介紹
1.1?函數原型
1.2?功能詳解
2 使用方法
2.1 創建流程
2.1.1 創建擴展廣播實例
2.1.2?設置周期性廣播數據
2.1.3?配置周期性廣播參數
2.1.4?啟動廣播
2.2 主流程函數
2.3?關鍵配置 (prj.conf)
3?高級用法
3.1?大數據分片傳輸
3.2?動態數據更新
3.3?多廣播集數據同步
4?應用場景
4.1?LE Audio 廣播
4.2?電子貨架標簽系統
4.3?工業傳感器網絡
5?性能優化技巧
5.1?數據壓縮:
5.2?增量更新:
5.3?自適應間隔:
6?常見問題和解決方案
概述
bt_le_per_adv_set_data()
?是 Zephyr RTOS 藍牙協議棧中用于設置周期性廣播數據的關鍵函數。它允許設備配置周期性廣播的數據內容,這是藍牙 5.0 及以上版本支持的高級特性。該函數是實現高效周期性廣播系統的核心,通過合理設計數據結構和更新機制,可以構建從低功耗傳感器網絡到高質量音頻廣播等各種應用。
1 函數接口介紹
1.1?函數原型
int bt_le_per_adv_set_data(struct bt_le_ext_adv *adv, const uint8_t *data, uint8_t len);
參數說明
參數 | 類型 | 說明 |
---|---|---|
adv | struct bt_le_ext_adv* | 指向已創建的擴展廣播實例的指針 |
data | const uint8_t* | 周期性廣播數據緩沖區 |
len | uint8_t | 數據長度(最大 252 字節) |
返回值
返回值 | 說明 |
---|---|
0 | 設置成功 |
-EINVAL | 無效參數 |
-ENOMEM | 內存不足 |
-EIO | 底層 HCI 錯誤 |
1.2?功能詳解
1)?配置周期性廣播內容
定義周期性廣播中傳輸的實際數據內容
數據格式遵循標準藍牙 AD 結構(長度-類型-值)
2)?與主廣播分離
周期性廣播數據獨立于主擴展廣播數據
允許傳輸更大、更頻繁更新的數據集
3)??數據格式要求
必須符合藍牙 SIG 定義的 AD 數據結構
典型結構:
// 示例數據格式 uint8_t per_adv_data[] = {0x02, BT_DATA_FLAGS, BT_LE_AD_NO_BREDR, // 廣播標志0x0A, BT_DATA_MANUFACTURER_DATA, 0xCD, 0x01, ... // 廠商自定義數據0x10, BT_DATA_SVC_DATA16, 0xAA, 0xFE, ... // 服務數據 };
2 使用方法
2.1 創建流程
2.1.1 創建擴展廣播實例
#include <zephyr/bluetooth/bluetooth.h>
#include <zephyr/bluetooth/gap.h>static struct bt_le_ext_adv *adv;// 創建擴展廣播實例
int create_adv_instance(void)
{struct bt_le_adv_param adv_params = {.id = BT_ID_DEFAULT,.sid = 1,.secondary_max_skip = 0,.options = BT_LE_ADV_OPT_EXT_ADV | BT_LE_ADV_OPT_PERIODIC,.interval_min = BT_GAP_ADV_SLOW_INT_MIN,.interval_max = BT_GAP_ADV_SLOW_INT_MAX,};return bt_le_ext_adv_create(&adv_params, NULL, &adv);
}
2.1.2?設置周期性廣播數據
// 設置周期性廣播數據
int set_periodic_data(void)
{// 示例:溫度傳感器數據uint8_t temp_data[] = {0x02, BT_DATA_FLAGS, BT_LE_AD_NO_BREDR, // 廣播標志0x03, BT_DATA_TEMP_SERVICE, 0xAA, 0xBB, // 溫度服務標識0x05, BT_DATA_SENSOR_VALUE, 0x00, 0x00, 0x00, 0x00 // 4字節溫度值};// 更新溫度值(小端格式)float temperature = 23.5f;memcpy(&temp_data[8], &temperature, sizeof(float));return bt_le_per_adv_set_data(adv, temp_data, sizeof(temp_data));
}
2.1.3?配置周期性廣播參數
// 配置周期性廣播參數
int config_periodic_adv(void)
{struct bt_le_per_adv_param per_adv_params = {.interval_min = BT_GAP_PER_ADV_SLOW_INT_MIN, // 1.28秒.interval_max = BT_GAP_PER_ADV_SLOW_INT_MIN, // 固定間隔.options = BT_LE_PER_ADV_OPT_USE_TX_POWER, // 包含發射功率};return bt_le_per_adv_set_param(adv, &per_adv_params);
}
2.1.4?啟動廣播
// 啟動廣播
int start_advertising(void)
{int err;// 啟動擴展廣播struct bt_le_ext_adv_start_param ext_adv_params = {0};if ((err = bt_le_ext_adv_start(adv, &ext_adv_params))) {return err;}// 啟動周期性廣播return bt_le_per_adv_start(adv);
}
2.2 主流程函數
void main(void)
{bt_enable(NULL); // 初始化藍牙create_adv_instance();set_periodic_data();config_periodic_adv();start_advertising();while (1) {k_sleep(K_SECONDS(30));update_sensor_data(); // 定期更新廣播數據}
}// 更新傳感器數據
void update_sensor_data(void)
{// 1. 停止周期性廣播bt_le_per_adv_stop(adv);// 2. 更新數據set_periodic_data();// 3. 重新啟動bt_le_per_adv_start(adv);
}
2.3?關鍵配置 (prj.conf)
# 基礎藍牙配置
CONFIG_BT=y
CONFIG_BT_DEBUG_LOG=y# 擴展廣播和周期性廣播
CONFIG_BT_EXT_ADV=y
CONFIG_BT_PER_ADV=y# 增加緩沖區大小
CONFIG_BT_BUF_ACL_TX_SIZE=255
CONFIG_BT_BUF_ACL_RX_SIZE=255# 支持的最大廣播集
CONFIG_BT_EXT_ADV_MAX_ADV_SET=2
3?高級用法
3.1?大數據分片傳輸
#define CHUNK_SIZE 200
uint8_t large_data[1000]; // >252字節的數據void send_large_data(void)
{for (int i = 0; i < sizeof(large_data); i += CHUNK_SIZE) {uint8_t chunk_len = MIN(CHUNK_SIZE, sizeof(large_data) - i);uint8_t adv_data[2 + CHUNK_SIZE] = {0x01, BT_DATA_SEQ_DATA, // 自定義分片標識(i / CHUNK_SIZE) & 0xFF // 分片序號};memcpy(&adv_data[2], &large_data[i], chunk_len);bt_le_per_adv_set_data(adv, adv_data, chunk_len + 2);k_sleep(K_MSEC(50)); // 等待傳輸完成}
}
3.2?動態數據更新
// 無中斷更新數據
void update_data_seamlessly(void)
{// 1. 準備新數據uint8_t new_data[200] = {...};// 2. 設置新數據bt_le_per_adv_set_data(adv, new_data, sizeof(new_data));// 注意:無需停止/重啟廣播,數據會自動更新
}
3.3?多廣播集數據同步
struct bt_le_ext_adv *adv1, *adv2;void sync_adv_data(void)
{uint8_t shared_data[] = {...};// 為多個廣播集設置相同數據bt_le_per_adv_set_data(adv1, shared_data, sizeof(shared_data));bt_le_per_adv_set_data(adv2, shared_data, sizeof(shared_data)));
}
4?應用場景
4.1?LE Audio 廣播
// 配置LE音頻廣播數據
int setup_le_audio_broadcast(void)
{uint8_t audio_data[] = {// 基礎音頻配置0x06, BT_DATA_LE_AUDIO_BROADCAST, 0x01, 0x00, // 廣播ID0x02, // 音頻編碼: LC30x02, 0x01, // 采樣率48kHz// 附加信息0x05, BT_DATA_LE_AUDIO_METADATA,0x01, 0x02, 0x03, 0x04};return bt_le_per_adv_set_data(adv, audio_data, sizeof(audio_data));
}
4.2?電子貨架標簽系統
void update_esl_data(uint16_t product_id, uint32_t price)
{uint8_t esl_data[] = {0x01, 0x12, // 自定義ESL標識(product_id >> 8) & 0xFF, product_id & 0xFF,(price >> 24) & 0xFF, (price >> 16) & 0xFF,(price >> 8) & 0xFF, price & 0xFF};bt_le_per_adv_set_data(adv, esl_data, sizeof(esl_data));
}
4.3?工業傳感器網絡
void broadcast_sensor_readings(float temp, float humidity, uint32_t pressure)
{uint8_t sensor_data[15] = {0x02, BT_DATA_MANUFACTURER_DATA, 0xCD, 0x01, // 廠商ID};// 小端格式打包數據memcpy(&sensor_data[4], &temp, 4);memcpy(&sensor_data[8], &humidity, 4);memcpy(&sensor_data[12], &pressure, 4);bt_le_per_adv_set_data(adv, sensor_data, sizeof(sensor_data));
}
5?性能優化技巧
5.1?數據壓縮:
// 使用緊湊數據格式
uint8_t compressed_data[10] = {0x01, 0xAB, // 數據類型標識(int8_t)(temp * 2), // 1字節溫度 (-128~127°C, 0.5°C精度)(uint8_t)humidity, // 1字節濕度 (0-100%)(pressure >> 16) & 0xFF, (pressure >> 8) & 0xFF, pressure & 0xFF // 3字節壓力
};
5.2?增量更新:
// 僅發送變化的數據
if (fabs(temp - last_temp) > 0.1f) {update_temp_in_adv_data(temp);bt_le_per_adv_set_data(adv, current_data, data_len);last_temp = temp;
}
5.3?自適應間隔:
// 根據數據變化率調整廣播間隔
void adjust_broadcast_interval(float change_rate)
{uint32_t new_interval = MAX(BT_GAP_PER_ADV_FAST_INT_MIN,MIN(BT_GAP_PER_ADV_SLOW_INT_MAX,1000 / change_rate // 基于變化率計算));struct bt_le_per_adv_param params = {.interval_min = new_interval,.interval_max = new_interval};bt_le_per_adv_set_param(adv, ¶ms);
}
6?常見問題和解決方案
1)?數據設置失敗 (錯誤 -22/EINVAL)
檢查數據長度:確保不超過 252 字節
驗證數據結構:確認 AD 結構格式正確(長度值匹配后續數據)
確認廣播實例狀態:必須在啟動周期性廣播前設置數
2)?接收端無法解析數據
包含必要標識:確保數據中包含廣播標志 (0x01)
添加設備名稱:包含完整或簡稱設備名
使用標準數據類型:優先使用?SIG 定義的 AD 類型
3)?數據更新延遲
減少廣播間隔:使用更短的間隔參數
優化更新流程:
// 高效更新流程 void efficient_update(uint8_t *new_data, uint8_t len) {// 無需停止廣播即可更新數據bt_le_per_adv_set_data(adv, new_data, len);// 強制刷新(某些控制器需要)bt_le_per_adv_stop(adv);k_sleep(K_MSEC(5));bt_le_per_adv_start(adv); }