ESP-NOW发送数据时如何传输长度未知的动态数组
核心错误说明
- 结构体中声明的无长度数组是C语言柔性数组成员,其本身不占用结构体的固定存储空间,直接计算
sizeof(test_struct)不会包含数组部分长度,若直接发送结构体实例,仅会发送结构体其余固定字段的内容,后续数组数据不会被带入发送流程。 - 你后续尝试的直接发送指针的写法存在本质错误:
by是指向堆内存的指针变量,&by取到的是指针变量自身的栈地址,传入的发送长度为sizeof(double)仅8字节,实际发送的是指针变量值的前8位,完全没有携带malloc分配的数组内的真实数据。且跨设备通信时,发送本设备的内存地址没有任何意义,接收端无法访问发送端的独立地址空间,自然无法得到正确数值。
可行实现方案
ESP-NOW 本身不强制要求使用固定长度结构体发包,发送接口仅要求传入数据起始地址与实际发送长度,只要单包长度不超过当前SDK配置的ESP-NOW单包上限(常规配置为250字节,开启长包模式后最高支持1490字节),即可正常发送可变长度数据,以下是两种常用实现方式:
方案1:长度前缀+连续数据发包(无结构体实现)
在待发送数据的最前端固定占用2字节(数组长度不超过65535时足够使用)存储数组实际长度,后续直接拼接数组元素即可,无需提前定义结构体。
发送端示例代码
int arr_len = 100; double *send_arr = (double*)malloc(arr_len * sizeof(double)); for (int i = 0; i < arr_len; i++) { send_arr[i] = i; Serial.println(send_arr[i]); } // 计算总发送长度:2字节长度字段 + 所有数组元素长度 size_t total_len = sizeof(uint16_t) + arr_len * sizeof(double); uint8_t *send_buf = (uint8_t*)malloc(total_len); // 写入长度字段 memcpy(send_buf, &arr_len, sizeof(uint16_t)); // 写入数组实际数据 memcpy(send_buf + sizeof(uint16_t), send_arr, arr_len * sizeof(double)); // 替换为实际对端MAC地址即可发送 esp_err_t result = esp_now_send(peer_mac, send_buf, total_len); // 发送完成后释放内存 free(send_buf); free(send_arr);
接收端示例代码
void on_esp_now_recv(const uint8_t *mac, const uint8_t *incoming_data, int len) { // 先读取前2字节的长度信息 uint16_t arr_len; memcpy(&arr_len, incoming_data, sizeof(uint16_t)); // 长度校验,避免越界 if (len != sizeof(uint16_t) + arr_len * sizeof(double)) { Serial.println("数据包格式错误"); return; } // 直接读取后续数组内容 double *recv_arr = (double*)(incoming_data + sizeof(uint16_t)); Serial.print("rearray=: "); for (int i = 0; i < arr_len; i++) { Serial.print(recv_arr[i]); Serial.print(" "); } Serial.println(); }
方案2:正确使用柔性数组结构体
如果习惯结构体编码风格,柔性数组可以正常使用,但必须满足两个要求:柔性数组必须是结构体最后一个成员、分配内存时要额外给柔性数组预留足够空间。
// 结构体定义 typedef struct { uint16_t arr_len; // 长度字段必须放在柔性数组之前 double arry[]; // 柔性数组,必须为最后一个成员 } test_struct; // 发送端内存分配与发包 test_struct *send_pack = (test_struct*)malloc(sizeof(test_struct) + arr_len * sizeof(double)); send_pack->arr_len = arr_len; for (int i = 0; i < arr_len; i++) { send_pack->arry[i] = i; } // 发送长度必须包含柔性数组部分的长度 esp_now_send(peer_mac, (uint8_t*)send_pack, sizeof(test_struct) + arr_len * sizeof(double)); free(send_pack);
该方案的内存布局和方案1完全一致,接收端逻辑无需修改。
注意事项
- 若待发送的数组总长度超过ESP-NOW单包上限,需要自行实现分包、重组逻辑,ESP-NOW本身不提供分片传输能力。
- 所有待发送数据必须存储在连续的内存块中,不能直接发送二级指针、分散的内存块,否则会读取到错误内存数据。
- 接收回调中传入的
incoming_data指针仅在回调执行期间有效,若需要长期存储接收数据,要在回调内将数据拷贝到自己分配的内存中。
内容的提问来源于stack exchange,提问作者lena
相关产品推荐
相关产品推荐

