BLE Mesh 消息可靠传输实现指南
根据您提供的文档信息,我来详细解释如何实现消息不丢包和如何发送无应答消息。
1. 实现消息传输不丢包(有应答消息)
要实现可靠传输不丢包,需要使用有应答的消息机制。以下是具体实现方法:
1.1 配置超时时间
首先在 menuconfig 中配置默认超时时间:
```
Component config → Bluetooth Mesh → Client model message timeout (seconds)
```
或者直接修改 sdkconfig:
```c
CONFIG_BLE_MESH_CLIENT_MSG_TIMEOUT=10 # 设置为10秒超时
```
1.2 发送有应答消息
```c
/**
* @brief 发送有应答的厂商消息(确保可靠传输)
*
* @param server_addr 目标地址
* @param data 发送数据
* @param length 数据长度
* @param opcode 操作码
* @param timeout_ms 自定义超时时间(毫秒),0表示使用默认值
* @return esp_err_t 发送结果
*/
esp_err_t example_ble_mesh_send_reliable_message(uint16_t server_addr, uint8_t *data, uint16_t length, uint32_t opcode, uint32_t timeout_ms)
{
esp_ble_mesh_msg_ctx_t ctx = {0};
esp_err_t err;
// 设置消息上下文
ctx.net_idx = gStore.net_idx;
ctx.app_idx = gStore.app_idx;
ctx.addr = server_addr;
ctx.send_ttl = MSG_SEND_TTL;
ctx.send_rel = true; // 要求可靠传输
if (opcode == 0) {
opcode = ESP_BLE_MESH_VND_MODEL_OP_SEND;
}
// 转换超时时间(毫秒到毫秒,但API可能需要不同的单位)
int msg_timeout = (timeout_ms > 0) ? (timeout_ms / 1000) : 0;
ESP_LOGI(BLE_MESH, "Sending reliable message to 0x%04x, timeout: %d ms", server_addr,
(timeout_ms > 0) ? timeout_ms : CONFIG_BLE_MESH_CLIENT_MSG_TIMEOUT * 1000);
// 发送有应答的消息(need_rsp = true)
err = esp_ble_mesh_client_model_send_msg(vendor_client.model,
&ctx,
opcode,
length,
data,
msg_timeout, // 超时时间
true, // need_rsp = true 要求应答
MSG_ROLE);
if (err != ESP_OK) {
ESP_LOGE(BLE_MESH, "Failed to send reliable message: %s", esp_err_to_name(err));
}
return err;
}
```
1.3 处理应答和超时事件
需要在Mesh事件回调中处理应答和超时:
```c
static void mesh_event_cb(esp_ble_mesh_cb_event_t event, esp_ble_mesh_cb_param_t *param)
{
switch (event) {
case ESP_BLE_MESH_CLIENT_MODEL_SEND_TIMEOUT_EVT:
// 消息发送超时处理
ESP_LOGE(BLE_MESH, "Message timeout, opcode: 0x%06x, addr: 0x%04x",
param->client_recv_timeout.opcode,
param->client_recv_timeout.ctx->addr);
// 这里可以触发重试机制
break;
case ESP_BLE_MESH_CLIENT_MODEL_RECV_RSP_EVT:
// 收到应答处理
ESP_LOGI(BLE_MESH, "Received response, opcode: 0x%06x, addr: 0x%04x",
param->client_recv_rsp.opcode,
param->client_recv_rsp.ctx->addr);
// 处理应答数据
ESP_LOG_BUFFER_HEX_LEVEL(BLE_MESH,
param->client_recv_rsp.params,
param->client_recv_rsp.length,
ESP_LOG_DEBUG);
break;
// 其他事件处理...
}
}
```
1.4 带自动重试的可靠发送函数
```c
/**
* @brief 带自动重试的可靠消息发送
*/
esp_err_t example_ble_mesh_send_with_retry(uint16_t server_addr, uint8_t *data, uint16_t length,
uint32_t opcode, uint8_t max_retries, uint32_t retry_delay_ms)
{
esp_err_t err;
uint8_t retry_count = 0;
do {
err = example_ble_mesh_send_reliable_message(server_addr, data, length, opcode, 5000);
if (err == ESP_OK) {
// 发送成功,等待应答或超时事件
// 这里需要实现一个等待机制,比如使用信号量或消息队列
// 等待 ESP_BLE_MESH_CLIENT_MODEL_RECV_RSP_EVT 事件
ESP_LOGI(BLE_MESH, "Message sent successfully, waiting for response...");
return ESP_OK;
}
retry_count++;
if (retry_count <= max_retries) {
ESP_LOGW(BLE_MESH, "Send failed, retry %d/%d after %d ms",
retry_count, max_retries, retry_delay_ms);
vTaskDelay(pdMS_TO_TICKS(retry_delay_ms));
}
} while (retry_count <= max_retries);
ESP_LOGE(BLE_MESH, "All retries failed for message to 0x%04x", server_addr);
return err;
}
```
1. 发送无应答的消息
2.1 客户端模型发送无应答消息
```c
/**
* @brief 发送无应答的厂商消息(快速,不保证可靠)
*
* @param server_addr 目标地址
* @param data 发送数据
* @param length 数据长度
* @param opcode 操作码
* @return esp_err_t 发送结果
*/
esp_err_t example_ble_mesh_send_unreliable_message(uint16_t server_addr, uint8_t *data, uint16_t length, uint32_t opcode)
{
esp_ble_mesh_msg_ctx_t ctx = {0};
esp_err_t err;
// 设置消息上下文
ctx.net_idx = gStore.net_idx;
ctx.app_idx = gStore.app_idx;
ctx.addr = server_addr;
ctx.send_ttl = MSG_SEND_TTL;
ctx.send_rel = false; // 不要求可靠传输
if (opcode == 0) {
opcode = ESP_BLE_MESH_VND_MODEL_OP_SEND;
}
ESP_LOGI(BLE_MESH, "Sending unreliable message to 0x%04x", server_addr);
// 发送无应答的消息(need_rsp = false)
err = esp_ble_mesh_client_model_send_msg(vendor_client.model,
&ctx,
opcode,
length,
data,
0, // 超时时间设为0
false, // need_rsp = false 不要求应答
MSG_ROLE);
if (err != ESP_OK) {
ESP_LOGE(BLE_MESH, "Failed to send unreliable message: %s", esp_err_to_name(err));
} else {
ESP_LOGD(BLE_MESH, "Unreliable message sent successfully");
}
return err;
}
```
2.2 服务器模型发送消息(总是无应答)
```c
/**
* @brief 服务器模型发送消息(总是无应答)
*
* 服务器模型发送的消息总是无应答的,适用于状态通知等场景
*
* @param model 服务器模型指针
* @param ctx 消息上下文
* @param opcode 操作码
* @param data 发送数据
* @param length 数据长度
* @return esp_err_t 发送结果
*/
esp_err_t example_ble_mesh_server_send_message(esp_ble_mesh_model_t *model,
esp_ble_mesh_msg_ctx_t *ctx,
uint32_t opcode,
uint8_t *data,
uint16_t length)
{
esp_err_t err;
if (model == NULL || ctx == NULL || data == NULL || length == 0) {
return ESP_ERR_INVALID_ARG;
}
ESP_LOGI(BLE_MESH, "Server sending message to 0x%04x", ctx->addr);
// 服务器模型发送消息(总是无应答)
err = esp_ble_mesh_server_model_send_msg(model, ctx, opcode, length, data);
if (err != ESP_OK) {
ESP_LOGE(BLE_MESH, "Server failed to send message: %s", esp_err_to_name(err));
} else {
ESP_LOGD(BLE_MESH, "Server message sent successfully");
}
return err;
}
```
1. 完整的使用示例
```c
// 初始化Mesh回调
esp_ble_mesh_register_prov_callback(mesh_event_cb);
// 发送有应答的可靠消息(不丢包)
uint8_t important_data[] = {0x01, 0x02, 0x03};
esp_err_t err = example_ble_mesh_send_reliable_message(0x1001, important_data, sizeof(important_data), 0, 5000);
if (err == ESP_OK) {
ESP_LOGI(BLE_MESH, "Reliable message sent, waiting for response...");
}
// 发送无应答的快速消息
uint8_t status_data[] = {0xAA, 0xBB};
err = example_ble_mesh_send_unreliable_message(0x1002, status_data, sizeof(status_data), 0);
if (err == ESP_OK) {
ESP_LOGI(BLE_MESH, "Unreliable message sent successfully");
}
// 服务器发送状态通知
esp_ble_mesh_msg_ctx_t ctx = {
.net_idx = gStore.net_idx,
.app_idx = gStore.app_idx,
.addr = 0xFFFF, // 广播地址
.send_ttl = 3,
.send_rel = false
};
uint8_t notification_data[] = {0x55, 0x66};
err = example_ble_mesh_server_send_message(vendor_server_model, &ctx, 0x2001, notification_data, sizeof(notification_data));
```
1. 选择策略建议
2. 使用有应答消息的场景: · 关键控制指令(开关、调节等) · 需要确认执行结果的操作 · 数据传输完整性要求高的场景
3. 使用无应答消息的场景: · 状态通知和心跳包 · 实时传感器数据(可以容忍偶尔丢失) · 广播消息和多播消息 · 对延迟敏感的应用
4. 超时时间设置建议: · 局域网内:2-5秒 · 多跳网络:5-10秒 · 高延迟网络:10-30秒
这样的实现可以确保在需要可靠性的场景下不丢包,同时在追求效率的场景下快速发送消息。 能够解决节点“ignoring old seqauth" 指南中api貌似不支持序列号持久化管理
最新发布