蓝牙
gatt server的service profile的数量最大可以到多少?如何设置?
在menuconfig(Component config → Bluetooth → Bluedroid Options → Bluetooth Low Energy → Include GATT server module(GATTS))中设置具体的数值,该数值范围是:1-32。
已经在 ESP32 端修改了 BLE 设备名称,但手机扫描到的仍然是旧名称,为什么没有生效?
这是正常现象,主要原因是手机端对 BLE 广播信息存在缓存机制。
在 BLE 中,设备名称通常通过广播包(Advertising Data)或扫描响应包(Scan Response)发送,手机在首次扫描到设备后,往往会缓存该设备的名称,用于后续快速显示。
即使 ESP32 端已经更新了设备名称,如果:
- 广播未重新开始
- 广播内容未发生明显变化
- 手机未主动刷新缓存
手机端仍可能显示旧的蓝牙名称。
建议处理方式:
- 在修改设备名称后,停止并重新开始 BLE 广播
- 确保新的设备名称已重新写入广播数据或扫描响应数据
- 确认名称设置时机正确
- 在开始广播前完成设备名称设置
- 避免在广播过程中动态修改名称但不重启广播
- 手机端操作
- 关闭并重新打开手机蓝牙
- 或清除系统蓝牙缓存后重新扫描
- 补充说明
- 不同手机厂商和系统版本的缓存策略不同,表现可能存在差异
ESP32-C2/C3/S3如何启用 BLE 2M PHY?
1.ESP32-C3 在使用 Bluedroid 协议栈时,BLE 的 PHY 默认是 1M。
Bluedroid 下如何切换到 2M PHY:
- 在
ESP_GATTS_CONNECT_EVT事件回调后调用以下接口,设置首选 PHY 为 2M:
esp_ble_gap_set_prefered_default_phy(
ESP_BLE_GAP_PHY_2M_PREF_MASK,
ESP_BLE_GAP_PHY_2M_PREF_MASK
);
- 适用版本:ESP-IDF 4.4 及 5.x 均支持。
- 说明:仅设置“首选 PHY”,最终是否切换成功取决于对端是否支持 2M PHY。
2.ESP32-C3 在使用 Nimble协议栈时,可在menuconfig中使能2M PHY
Component config → Bluetooth → NimBLE Options → BLE 5.x Features → Enable BLE 5 feature

基于ble_spp_server示例程序,默认将 MTU 设置为 512。手机 APP 与 ESP32C2 建立蓝牙连接后,APP 也将 MTU 设置为 512。但当 ESP32C2 向 APP 发送 200 字节左右数据(未超过 MTU)时,数据仍被拆分成 3 个包。这是为什么?请问如何能让这些数据一次性完整接收,而不是被拆包?
在 BLE SPP Server 例程 uart_task 代码中,模块采用如下透传发送逻辑:串口助手一次性发送一大段数据(如500字节)到模块串口。模块的串口接收是分段触 发的(比如缓冲区满、定时中断等原因),每次触发到的数据长度通常较短(几十到几百字节不等),每段被逐次缓存到模块端。每收到一段数据(每次UART 事件),例程代码就马上通过esp_ble_gatts_send_indicate将这一段数据透传给APP。
所以无论MTU设置多大,只要串口缓冲或事件分段,APP都会收到多条碎片包,而不是一次完整数据包。这就是“分包”现象的本质原因。
建议是在模块侧新增聚包处理逻辑,把连续收到的数据先放到缓冲区里,直到遇到特定条件(比如缓冲区满、或检测到结束符\n),才整体一次性发送整个缓冲 区内容到APP。这样即使串口助手分段发送或者UART事件分段触发,最终只会收到一条完整包(如APP端500字节一次收齐)。
// 1. 增加灵活聚包缓存以及结束符(可以自定义)
#define SPP_BLE_BUF_SIZE 600
#define SPP_BLE_END_CHAR '\n'
static uint8_t spp_ble_cache[SPP_BLE_BUF_SIZE];
static uint16_t spp_ble_cache_len = 0;
// 2. 修改uart_task,灵活聚包,遇结束符BLE通知发送
void uart_task(void *pvParameters)
{
uart_event_t event;
for (;;) {
if (xQueueReceive(spp_uart_queue, (void *)&event, (TickType_t)portMAX_DELAY)) {
switch (event.type) {
case UART_DATA:
if (event.size && is_connected) {
uint8_t *temp = (uint8_t *)malloc(event.size);
if (!temp) {
ESP_LOGE(GATTS_TABLE_TAG, "%s malloc failed", __func__);
break;
}
uart_read_bytes(UART_NUM_0, temp, event.size, portMAX_DELAY);
// 灵活聚包处理
for (int i = 0; i < event.size; ++i) {
// 追加进聚包缓存
if (spp_ble_cache_len < SPP_BLE_BUF_SIZE) {
spp_ble_cache[spp_ble_cache_len++] = temp[i];
// 判定遇到结束符则一次通知BLE端
if (temp[i] == SPP_BLE_END_CHAR) {
// BLE Notify使能检测
if (enable_data_ntf) {
ESP_LOGI(GATTS_TABLE_TAG, "BLE聚合包,缓存长度=%d,MTU=%d", spp_ble_cache_len, spp_mtu_size);
esp_ble_gatts_send_indicate(
spp_gatts_if,
spp_conn_id,
spp_handle_table[SPP_IDX_SPP_DATA_NTY_VAL],
spp_ble_cache_len,
spp_ble_cache,
false
);
}
spp_ble_cache_len = 0; // 通知后清空缓存
}
} else {
ESP_LOGW(GATTS_TABLE_TAG, "BLE串口缓存溢出,自动清零!");
spp_ble_cache_len = 0;
}
}
free(temp);
}
break;
default:
break;
}
}
}
vTaskDelete(NULL);
}