walker2048 发表于 2024-6-5 23:29

【FireBeetle 2 ESP32 C6开发板】2、ESPNOW性能测试--带宽测试

<div class='showpostmsg'>### 前言
ESP-NOW 是乐鑫定义的一种无线通信协议,能够在无路由器的情况下直接、快速、低功耗地控制智能设备。它能够与 Wi-Fi 和 Bluetooth LE 共存,支持乐鑫 ESP8266、ESP32、ESP32-S 和 ESP32-C 等多系列 SoC。ESP-NOW 广泛应用于智能家电、远程控制和传感器等领域。

我们在使用ESP-NOW的时候,都非常想知道它的性能指标,例如带宽、延迟之类的。在查看espnow仓库源码的时候,正好发现了相关的测试代码,我们可以来看一下。

### 用于测试ESP-NOW性能指标的源码
在ESPNOW仓库源码里,可以找到测试相关的代码(components/esp-now/src/debug/src/commands/cmd_iperf.c)
```c
static void espnow_iperf_initiator_task(void *arg)
{
    esp_err_t ret   = ESP_OK;
    espnow_iperf_data_t *iperf_data = ESP_CALLOC(1, g_iperf_cfg.packet_len);
    iperf_data->type = IPERF_BANDWIDTH;
    iperf_data->seq   = 0;


    int64_t start_time = esp_timer_get_time();
    int64_t end_time = start_time + g_iperf_cfg.transmit_time * 1000 * 1000;
    uint32_t total_count   = 0;

    if (!g_iperf_cfg.frame_head.broadcast) {
      espnow_add_peer(g_iperf_cfg.addr, NULL);
    }

    ESP_LOGI(TAG, "   Interval   Transfer   Frame_rate   Bandwidth");

    for (int64_t report_time = start_time + g_iperf_cfg.report_interval * 1000 * 1000, report_count = 0;
            esp_timer_get_time() < end_time && !g_iperf_cfg.finish;) {
      ret = espnow_send(g_iperf_cfg.type, g_iperf_cfg.addr, iperf_data,
                        g_iperf_cfg.packet_len, &g_iperf_cfg.frame_head, portMAX_DELAY);
      ESP_ERROR_CONTINUE(ret != ESP_OK && ret != ESP_ERR_WIFI_TIMEOUT, "<%s> espnow_send", esp_err_to_name(ret));
      iperf_data->seq++;
      ++total_count;

      if (esp_timer_get_time() >= report_time) {
            uint32_t report_time_s = (report_time - start_time) / (1000 * 1000);
            double report_size    = (iperf_data->seq - report_count) * g_iperf_cfg.packet_len / 1e6;

            ESP_LOGI(TAG, "["MACSTR"]%2d-%2d sec%2.2f MBytes   %0.2f Hz%0.2f Mbps",
                     MAC2STR(g_iperf_cfg.addr), report_time_s - g_iperf_cfg.report_interval, report_time_s,
                     report_size, (iperf_data->seq - report_count) * 1.0 / g_iperf_cfg.report_interval, report_size * 8 / g_iperf_cfg.report_interval);

            report_time = esp_timer_get_time() + g_iperf_cfg.report_interval * 1000 * 1000;
            report_count = iperf_data->seq;
      }
    }

    iperf_data->type= IPERF_BANDWIDTH_STOP;
    int retry_count   = 5;
    wifi_pkt_rx_ctrl_t rx_ctrl = {0};
    uint32_t spend_time_ms = (esp_timer_get_time() - start_time) / 1000;

    do {
      ret = espnow_send(g_iperf_cfg.type, g_iperf_cfg.addr, iperf_data,
                        g_iperf_cfg.packet_len, &g_iperf_cfg.frame_head, portMAX_DELAY);
      ESP_ERROR_CONTINUE(ret != ESP_OK, "<%s> espnow_send", esp_err_to_name(ret));

      memset(iperf_data, 0, g_iperf_cfg.packet_len);
      iperf_recv_data_t recv_data = { 0 };
      if (g_iperf_queue && xQueueReceive(g_iperf_queue, &recv_data, pdMS_TO_TICKS(1000)) == pdPASS) {
            ret = ESP_OK;
            memcpy(iperf_data, recv_data.data, recv_data.size);
            memcpy(g_iperf_cfg.addr, recv_data.src_addr, 6);
            memcpy(&rx_ctrl, &recv_data.rx_ctrl, sizeof(wifi_pkt_rx_ctrl_t));
            ESP_FREE(recv_data.data);
      } else {
            ret = ESP_FAIL;
      }
    } while (ret != ESP_OK && retry_count-- > 0 && iperf_data->type != IPERF_BANDWIDTH_STOP_ACK);

    if (ret != ESP_OK) {
      ESP_LOGW(TAG, "<%s> Receive responder response failed", esp_err_to_name(ret));
    } else {
      uint32_t write_count = iperf_data->seq > 0 ? iperf_data->seq - 1 : 0;
      uint32_t lost_count= total_count - write_count;
      double total_len   = (total_count * g_iperf_cfg.packet_len) / 1e6;

      if (total_count && write_count && spend_time_ms) {
            ESP_LOGI(TAG, "initiator Report:");
            ESP_LOGI(TAG, "[ ID] Interval      Transfer       Bandwidth      Jitter   Lost/Total DatagramsRSSIChannel");
            ESP_LOGI(TAG, " %2d-%2d sec    %2.2f MBytes    %0.2f Mbps    %0.2f ms    %d/%d (%0.2f%%)    %d    %d",
                  0, spend_time_ms / 1000, total_len, total_len * 8 * 1000 / spend_time_ms, spend_time_ms * 1.0 / write_count,
                  lost_count, total_count, lost_count * 100.0 / total_count, rx_ctrl.rssi, rx_ctrl.channel);
      }
    }

    if (!g_iperf_cfg.frame_head.broadcast) {
      espnow_del_peer(g_iperf_cfg.addr);
    }

    ESP_FREE(iperf_data);
    g_iperf_cfg.finish = true;

    espnow_set_config_for_data_type(ESPNOW_DATA_TYPE_RESERVED, 0, NULL);
    if (g_iperf_queue) {
      iperf_recv_data_t tmp_data ={ 0 };

      while (xQueueReceive(g_iperf_queue, &tmp_data, 0)) {
            ESP_FREE(tmp_data.data);
      }

      vQueueDelete(g_iperf_queue);
      g_iperf_queue = NULL;
    }

    vTaskDelete(NULL);
}
```

这个函数的作用,是发起iperf测试任务,这个任务会向指定的地址发起测试,并打印相关的测试信息,例如传输数据的大小,传输带宽,信号强度等等内容。


-----------------------

光是一个设备跑这段代码也是没用的,我们需要另一个设备跑测试响应任务。测试响应任务也是在同一个文件里。
```c
static esp_err_t espnow_iperf_responder(uint8_t *src_addr, void *data,
                      size_t size, wifi_pkt_rx_ctrl_t *rx_ctrl)
{
    ESP_PARAM_CHECK(src_addr);
    ESP_PARAM_CHECK(data);
    ESP_PARAM_CHECK(size);
    ESP_PARAM_CHECK(rx_ctrl);

    esp_err_t ret         = ESP_OK;
    espnow_iperf_data_t *iperf_data   = (espnow_iperf_data_t *)data;
    static int64_t start_time;
    static uint32_t recv_count;
    static int64_t report_time;
    static uint32_t report_count;

    memcpy(g_iperf_cfg.addr, src_addr, 6);

    if (!g_iperf_cfg.finish) {
      recv_count++;

      if (iperf_data->seq == 0) {
            recv_count   = 0;
            start_time= esp_timer_get_time();
            report_time = start_time + g_iperf_cfg.report_interval * 1000 * 1000;
            report_count = 0;
      }

      if (iperf_data->type == IPERF_BANDWIDTH && esp_timer_get_time() >= report_time) {
            uint32_t report_time_s = (report_time - start_time) / (1000 * 1000);
            double report_size    = (recv_count - report_count) * size / 1e6;
            ESP_LOGI(TAG, "["MACSTR"]%2d-%2d sec%2.2f MBytes%0.2f Mbps%d dbm",
                     MAC2STR(g_iperf_cfg.addr), report_time_s - g_iperf_cfg.report_interval, report_time_s,
                     report_size, report_size * 8 / g_iperf_cfg.report_interval, rx_ctrl->rssi);

            report_time = esp_timer_get_time() + g_iperf_cfg.report_interval * 1000 * 1000;
            report_count = recv_count;
      } else if (iperf_data->type == IPERF_PING) {
            ESP_LOGV(TAG, "Recv IPERF_PING, seq: %d, recv_count: %d", iperf_data->seq, recv_count);
            iperf_data->type = IPERF_PING_ACK;

            if (g_iperf_cfg.gpio_num >= 0) {
                gpio_set_level(g_iperf_cfg.gpio_num, 0);
            }

            if (!g_iperf_cfg.frame_head.broadcast) {
                espnow_add_peer(g_iperf_cfg.addr, NULL);
            }

            ret = espnow_send(g_iperf_cfg.type, g_iperf_cfg.addr, iperf_data,
                              size, &g_iperf_cfg.frame_head, portMAX_DELAY);

            if (!g_iperf_cfg.frame_head.broadcast) {
                espnow_del_peer(g_iperf_cfg.addr);
            }

            if (g_iperf_cfg.gpio_num >= 0) {
                gpio_set_level(g_iperf_cfg.gpio_num, 1);
            }

            ESP_ERROR_RETURN(ret != ESP_OK, ret, "<%s> espnow_send", esp_err_to_name(ret));
      } else if (iperf_data->type == IPERF_BANDWIDTH_STOP) {
            uint32_t total_count = iperf_data->seq + 1;
            uint32_t lost_count= total_count - recv_count;
            double total_len   = (total_count * size) / 1e6;
            uint32_t spend_time_ms= (esp_timer_get_time() - start_time) / 1000;

            ESP_LOGI(TAG, "[ ID] Interval      Transfer       Bandwidth      Jitter   Lost/Total Datagrams");
            ESP_LOGI(TAG, " %2d-%2d sec    %2.2f MBytes    %0.2f Mbps    %0.2f ms    %d/%d (%0.2f%%)",
                     0, spend_time_ms / 1000, total_len, total_len * 8 * 1000 / spend_time_ms, spend_time_ms * 1.0 / recv_count,
                     lost_count, total_count, lost_count * 100.0 / total_count);

            iperf_data->seq = recv_count;
            iperf_data->type = IPERF_BANDWIDTH_STOP_ACK;
            ESP_LOGD(TAG, "iperf_data->seq: %d",iperf_data->seq);

            espnow_frame_head_t frame_head = {
                .filter_adjacent_channel = true,
            };

            espnow_add_peer(g_iperf_cfg.addr, NULL);
            ret = espnow_send(g_iperf_cfg.type, g_iperf_cfg.addr, iperf_data,
                              sizeof(espnow_iperf_data_t), &frame_head, portMAX_DELAY);
            ESP_ERROR_RETURN(ret != ESP_OK, ret, "<%s> espnow_send", esp_err_to_name(ret));

            espnow_del_peer(g_iperf_cfg.addr);
      }
    }

    return ESP_OK;
}
```

这个函数就是响应iperf测试的程序了。我们只需要在两个设备上按照调用方式修改好程序,就可以进行测试。以下是我的测试结果。


可以看到在1分钟时间内,总共发送了17888个数据包,丢了10个包,丢包率0.06%, 信号强度是-13左右(离的很近),带宽是0.51Mbps。
相对于wifi的带宽性能,espnow远距离模式的性能是比较弱的,但是考虑到ESPNOW不需要路由器,可以设备在未联网的时候,多设备互相发送数据,
并且这个速度也比常规串口115200的性能要强。那也不是不能接受了。

传输距离的话,我这边测试的情况大致如下,PCB天线的板子,基本上可以在穿1堵墙,距离20米内时,带宽维持在0.4Mbps,陶瓷天线的性能差一点,只能在10米内维持0.3Mbps的一个带宽。

</div><script>                                        var loginstr = '<div class="locked">查看本帖全部内容,请<a href="javascript:;"   style="color:#e60000" class="loginf">登录</a>或者<a href="https://bbs.eeworld.com.cn/member.php?mod=register_eeworld.php&action=wechat" style="color:#e60000" target="_blank">注册</a></div>';
                                       
                                        if(parseInt(discuz_uid)==0){
                                               
                                        }                </script><script type="text/javascript">(function(d,c){var a=d.createElement("script"),m=d.getElementsByTagName("script"),eewurl="//counter.eeworld.com.cn/pv/count/";a.src=eewurl+c;m.parentNode.insertBefore(a,m)})(document,523)</script>
页: [1]
查看完整版本: 【FireBeetle 2 ESP32 C6开发板】2、ESPNOW性能测试--带宽测试