You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.03 16:18:20