您的位置:首页 > 科技 > IT业 > 在线客服功能_机械加工怎么找客户_长沙网站优化体验_如何提高网站的搜索排名

在线客服功能_机械加工怎么找客户_长沙网站优化体验_如何提高网站的搜索排名

2025/1/8 18:43:56 来源:https://blog.csdn.net/Naiva/article/details/144937646  浏览:    关键词:在线客服功能_机械加工怎么找客户_长沙网站优化体验_如何提高网站的搜索排名
在线客服功能_机械加工怎么找客户_长沙网站优化体验_如何提高网站的搜索排名

BLE 每帧协议格式:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

问题:

ESP32设备通过BLE接收微信小程序端发送的数据,而且是多帧数据。

数据解析函数初始代码逻辑如下:


// 数据解析函数
void esp_ble_parse_data(uint8_t *frame)
{if (frame[0] != FRAME_HEADER || frame[53] != FRAME_TAIL) // 判断帧头/帧尾{ESP_LOGE(ESP_BLE_TAG, "Invalid frame header or tail");return;}esp_ble_data_frame_t *parsed_frame = (esp_ble_data_frame_t *)frame;// uint8_t computed_checksum = esp_ble_calculate_checksum(frame + 1, 52); // 校验和计算 1-54  0-53 0-52  数据长度 54 /// if (computed_checksum != parsed_frame->checksum)// {//     ESP_LOGE(ESP_BLE_TAG, "Checksum mismatch: expected 0x%02X, got 0x%02X", computed_checksum, parsed_frame->checksum);//     return;// }ESP_LOGI(ESP_BLE_TAG, "Valid frame received");ESP_LOGI(ESP_BLE_TAG, "Mode: 0x%02X, Data Length: %d, Interval: 0x%02X", parsed_frame->mode, parsed_frame->data_length, parsed_frame->interval);// 根据模式设置模式变量switch (parsed_frame->mode){case 0x01:t_show_mode = true;clear_erase_key(esp_nvs_keys[0]);clear_erase_key(esp_nvs_keys[2]);save_show_mode(esp_nvs_keys[0], t_show_mode); // 保存转灯的显示模式save_img_data(esp_nvs_keys[2], tsl_static_img, parsed_frame->data, sizeof(parsed_frame->data));ESP_LOGI(ESP_BLE_TAG, "转灯静图模式, t_show_mode = %d", t_show_mode);break;case 0x02:t_show_mode = false;clear_erase_key(esp_nvs_keys[0]);clear_erase_key(esp_nvs_keys[3]);save_show_mode(esp_nvs_keys[0], t_show_mode); // 保存转灯的显示模式save_img_data(esp_nvs_keys[3], tsl_dynamic_img, parsed_frame->data, sizeof(parsed_frame->data));ESP_LOGI(ESP_BLE_TAG, "转灯动态图模式, t_show_mode = %d", t_show_mode);break;case 0x03:d_show_mode = true;clear_erase_key(esp_nvs_keys[1]);clear_erase_key(esp_nvs_keys[4]);save_show_mode(esp_nvs_keys[1], d_show_mode); // 保存日行灯的显示模式save_img_data(esp_nvs_keys[4], drl_static_img, parsed_frame->data, sizeof(parsed_frame->data));ESP_LOGI(ESP_BLE_TAG, "日行灯静图模式, d_show_mode = %d", d_show_mode);xEventGroupSetBits(light_event_group, EVENT_RUNNING_LIGHT); // 触发日行灯显示刷新break;case 0x04:// d_show_mode = false;// clear_erase_key(esp_nvs_keys[1]);// save_show_mode(esp_nvs_keys[1], d_show_mode); // 保存日行灯的显示模式// // clear_erase_key(esp_nvs_keys[5]);// // save_img_data(esp_nvs_keys[5], drl_static_img, parsed_frame->data, sizeof(parsed_frame->data));// ESP_LOGI(ESP_BLE_TAG, "日行灯动态图模式, d_show_mode = %d", d_show_mode);// xEventGroupSetBits(light_event_group, EVENT_RUNNING_LIGHT); // 触发日行灯显示刷新/*********************************************/// uint8_t total_frames = (parsed_frame->data_length >> 4) & 0x0F; // 高位表示总包数 最大6// uint8_t frame_index = parsed_frame->data_length & 0x0F;         // 低位表示包序号 最小1// // 计算延时时间(单位:毫秒)// uint8_t drl_dynamic_img_delay_time = parsed_frame->interval * 100;// (void)drl_dynamic_img_delay_time; // 暂时忽略未使用警告// // 检查总包数和当前帧序号的有效性// if (total_frames > 6 || frame_index < 1 || frame_index > total_frames)// {//     ESP_LOGE(ESP_BLE_TAG, "Invalid frame index (%d) or total frames (%d)", frame_index, total_frames);//     return;// }// // 分配缓冲区// uint8_t *drl_dynamic_img_buffer = (uint8_t *)malloc(48 * total_frames);// if (!drl_dynamic_img_buffer)// {//     ESP_LOGE(ESP_BLE_TAG, "Failed to allocate memory for dynamic image buffer");//     return;// }// // 拷贝数据// memcpy(drl_dynamic_img_buffer + (frame_index - 1) * 48, parsed_frame->data, 48);// // 检查是否接收到所有帧// if (frame_index == total_frames)// {//     // drl_dynamic_img_delay = drl_dynamic_img_delay_time;//     d_show_mode = false;//     clear_erase_key(esp_nvs_keys[1]);//     clear_erase_key(esp_nvs_keys[5]);//     save_show_mode(esp_nvs_keys[1], d_show_mode); // 保存日行灯的显示模式//     save_img_data(esp_nvs_keys[5], drl_dynamic_img, drl_dynamic_img_buffer, 48 * total_frames);//     ESP_LOGI(ESP_BLE_TAG, "日行灯动态图模式, d_show_mode = %d", d_show_mode);//     xEventGroupSetBits(light_event_group, EVENT_RUNNING_LIGHT); // 触发日行灯显示刷新// }// free(drl_dynamic_img_buffer);// break;
/*********************************************/break;default:ESP_LOGE(ESP_BLE_TAG, "Unknown mode: 0x%02X", parsed_frame->mode);break;}
}

关键代码解释:

memcpy(drl_dynamic_img_buffer + (frame_index - 1) * 48, parsed_frame->data, 48);

这段代码的作用是将当前帧的数据复制到动态图片缓冲区(drl_dynamic_img_buffer)的正确位置。以下是对每个部分的详细解析:


代码的整体功能

memcpy(drl_dynamic_img_buffer + (frame_index - 1) * 48, parsed_frame->data, 48);
功能
  • 目的:将 parsed_frame->data 中的 48 字节数据复制到 drl_dynamic_img_buffer 的指定位置。
  • 场景drl_dynamic_img_buffer 是一个存储多帧动态图片数据的缓冲区,而 parsed_frame->data 是当前帧的数据。

代码分解与解释

1. memcpy 函数

memcpy 是 C 标准库中的内存拷贝函数,用于从源地址复制指定字节数的数据到目标地址。

  • 原型
    void *memcpy(void *dest, const void *src, size_t n);
    
    • dest:目标内存地址(复制到这里)。
    • src:源内存地址(从这里复制)。
    • n:需要复制的字节数。
2. drl_dynamic_img_buffer + (frame_index - 1) * 48

目标地址

  • drl_dynamic_img_buffer 是整个动态图片缓冲区的起始地址。
  • (frame_index - 1) 是帧的索引,从 0 开始计算偏移量。
  • 每帧数据占用 48 字节,因此偏移量需要乘以 48

计算结果:当前帧的数据存储到 drl_dynamic_img_buffer 偏移量为 (frame_index - 1) * 48 的位置。

3. parsed_frame->data

源地址

  • parsed_frame->data 是当前帧的数据指针,指向 48 字节的数据块。
  • 这些数据是从帧结构体中提取的,通常由某种协议或外部数据包解析得到。
4. 48

复制的字节数

  • 每帧固定大小为 48 字节,所以 memcpy 只复制 48 个字节的数据。

数据流示例

假设条件
  • drl_dynamic_img_buffer 大小为 288 字节(支持存储 6 帧,每帧 48 字节)。
  • 当前处理第 frame_index = 2 帧。
  • parsed_frame->data 包含 48 字节的内容,例如:[0x01, 0x02, ..., 0x30]
计算偏移量

目标地址:

drl_dynamic_img_buffer + (frame_index - 1) * 48
= drl_dynamic_img_buffer + (2 - 1) * 48
= drl_dynamic_img_buffer + 48

数据会被存储到缓冲区的第 48 字节位置(从索引 48 到 95)。


缓冲区内容更新

初始缓冲区(假设第 1 帧已存储 0xFF):

[0xFF, 0xFF, ..., 0xFF] (48 字节,帧 1)
[未初始化] (48 字节,帧 2)
[未初始化] (192 字节,其余帧)

复制后缓冲区内容:

[0xFF, 0xFF, ..., 0xFF] (48 字节,帧 1)
[0x01, 0x02, ..., 0x30] (48 字节,帧 2)
[未初始化] (192 字节,其余帧)

使用场景与意义

  • 多帧动态图片存储:动态图片可能由多个帧组成,每帧数据分包发送到设备。drl_dynamic_img_buffer 用于依次存储这些帧数据,最终形成完整的动态图。
  • 数据顺序管理:通过 (frame_index - 1) 确保每帧数据存储在正确的位置,避免数据覆盖或顺序错误。
  • 通用性强:通过计算偏移量和字节数,代码可支持任意数量的帧和大小。

总结

memcpy(drl_dynamic_img_buffer + (frame_index - 1) * 48, parsed_frame->data, 48);

作用:将当前帧的 48 字节数据复制到动态图片缓冲区中,存储在对应的帧位置。

核心逻辑

  1. 根据 frame_index 计算目标存储位置。
  2. 将当前帧的数据完整复制到缓冲区。
  3. 确保多帧数据按顺序存储在统一的缓冲区中,为后续处理或显示提供基础。

如果有更多细节需要补充,请随时告诉我!


代码逻辑分析与改进建议


代码功能分析

功能
  1. 解析数据包

    • total_frames:表示数据包总数(最大 6)。
    • frame_index:当前帧的序号(最小 1)。
  2. 检查数据有效性

    • 验证 total_framesframe_index 的范围。
  3. 动态分配缓冲区

    • 为总帧数据分配内存,大小为 48 * total_frames
  4. 存储帧数据

    • 将当前帧数据存储到动态缓冲区的正确位置。
  5. 接收完成时处理

    • 如果接收完成(即 frame_index == total_frames),保存数据并更新显示模式。
  6. 释放资源

    • 释放动态分配的缓冲区内存,避免内存泄漏。

关键问题与改进

问题 1:每次函数调用都会重新分配缓冲区
  • 当前代码中,缓冲区 drl_dynamic_img_buffer 每次调用函数都会重新分配,即使是处理同一个动态图片的数据包。
  • 这种做法导致内存分配重复且覆盖之前已接收的数据。
改进方法
  • 使用 静态缓冲区全局缓冲区,使所有帧数据共享一个缓冲区。
  • 示例:
static uint8_t *drl_dynamic_img_buffer = NULL; // 静态缓冲区
static uint8_t total_frames_allocated = 0;    // 记录已分配的帧数if (drl_dynamic_img_buffer == NULL) {drl_dynamic_img_buffer = (uint8_t *)malloc(48 * total_frames);if (!drl_dynamic_img_buffer) {ESP_LOGE(ESP_BLE_TAG, "Failed to allocate memory for dynamic image buffer");return;}total_frames_allocated = total_frames; // 记录分配的总帧数
} else if (total_frames != total_frames_allocated) {ESP_LOGE(ESP_BLE_TAG, "Inconsistent total_frames allocation");free(drl_dynamic_img_buffer);return;
}
问题 2:未处理数据包丢失情况
  • 当前逻辑假定所有数据包按顺序到达,未处理数据包丢失或乱序的问题。
  • 如果数据包丢失,最后一帧接收完成时可能会保存不完整的动态图。
改进方法
  • 增加接收状态标记,如使用一个布尔数组 frame_received[] 记录每帧接收状态。
  • 示例:
static bool frame_received[6] = {false}; // 最大支持 6 帧// 标记当前帧为已接收
frame_received[frame_index - 1] = true;// 检查所有帧是否接收完成
bool all_received = true;
for (int i = 0; i < total_frames; i++) {if (!frame_received[i]) {all_received = false;break;}
}if (all_received) {// 处理接收完成逻辑
}
问题 3:缓冲区释放时机不当
  • 缓冲区 drl_dynamic_img_buffer 在函数结束时总是释放,但在最后一帧时需要保留完整数据。
改进方法
  • 仅在数据接收完成并保存后释放缓冲区。
  • 示例:
if (frame_index == total_frames) {// 保存数据save_img_data(esp_nvs_keys[5], drl_dynamic_img, drl_dynamic_img_buffer, 48 * total_frames);// 释放缓冲区free(drl_dynamic_img_buffer);drl_dynamic_img_buffer = NULL; // 避免悬空指针
}

改进后的完整代码

以下是改进后的代码示例:

static uint8_t *drl_dynamic_img_buffer = NULL;
static uint8_t total_frames_allocated = 0;
static bool frame_received[6] = {false}; // 最大支持 6 帧void process_dynamic_img(parsed_frame_t *parsed_frame) {uint8_t total_frames = (parsed_frame->data_length >> 4) & 0x0F; // 高位表示总包数 最大6uint8_t frame_index = parsed_frame->data_length & 0x0F;         // 低位表示包序号 最小1// 检查总包数和当前帧序号的有效性if (total_frames > 6 || frame_index < 1 || frame_index > total_frames) {ESP_LOGE(ESP_BLE_TAG, "Invalid frame index (%d) or total frames (%d)", frame_index, total_frames);return;}// 初始化缓冲区if (drl_dynamic_img_buffer == NULL) {drl_dynamic_img_buffer = (uint8_t *)malloc(48 * total_frames);if (!drl_dynamic_img_buffer) {ESP_LOGE(ESP_BLE_TAG, "Failed to allocate memory for dynamic image buffer");return;}total_frames_allocated = total_frames;memset(frame_received, 0, sizeof(frame_received));// 初始化为 0} else if (total_frames != total_frames_allocated) {ESP_LOGE(ESP_BLE_TAG, "Inconsistent total_frames allocation");free(drl_dynamic_img_buffer);drl_dynamic_img_buffer = NULL;return;}// 拷贝当前帧数据memcpy(drl_dynamic_img_buffer + (frame_index - 1) * 48, parsed_frame->data, 48);frame_received[frame_index - 1] = true;// 检查所有帧是否接收完成bool all_received = true;for (int i = 0; i < total_frames; i++) {if (!frame_received[i]) {all_received = false;break;}}if (all_received) {// 保存数据clear_erase_key(esp_nvs_keys[1]);clear_erase_key(esp_nvs_keys[5]);save_show_mode(esp_nvs_keys[1], false);save_img_data(esp_nvs_keys[5], drl_dynamic_img, drl_dynamic_img_buffer, 48 * total_frames);ESP_LOGI(ESP_BLE_TAG, "日行灯动态图模式, d_show_mode = false");xEventGroupSetBits(light_event_group, EVENT_RUNNING_LIGHT); // 触发显示刷新// 释放缓冲区free(drl_dynamic_img_buffer);drl_dynamic_img_buffer = NULL;}
}

主要改进点

  1. 静态缓冲区:避免每次函数调用重复分配内存。
  2. 接收状态检查:确保所有帧完整接收后再处理。
  3. 释放时机优化:仅在数据完全保存后释放缓冲区。
  4. 错误处理:增加分配失败和状态不一致的错误检查。

通过这些改进,代码的内存管理更高效,逻辑更可靠。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com