avatar

松果工作室

欢迎光临

  • 首页
  • ESP
  • LVGL
  • freeRTOS
  • 快速笔记
  • 考察日志
  • 个人收藏
  • 我的服务
Home (ESP-IDF)ESPNOW
文章

(ESP-IDF)ESPNOW

Posted 2025-12-7 Updated 2025-12- 27
By YCP
15~19 min read

传输距离(外置天线/笔直的公路)
稳定距离:100 米
极限距离:370 米

帧格式

默认比特率为1Mbps
-----------------------------------------------------------------------
··MAC 报头··|··分类代码··|··组织标识符··|··随机值··|··供应商特定内容··|··FCS··
-----------------------------------------------------------------------
··24 字节······1 字节········3 字节······4 字节······7~255 字节······4···
-----------------------------------------------------------------------

  • 分类代码:分类代码:分类代码字段用于指示各个供应商的类别
  • 组织标识符:包含一个唯一的标识符,为乐鑫指定的MAC地址的前三个字节
  • 随机值:防止重放攻击
  • 供应商特定内容:供应商特定内容包含供应商特定字段,如下所示:

-----------------------------------------------------------------------
|···元素 ID···|···长度···|···组织标识符···|···类型···|···版本···|···正文···|
-----------------------------------------------------------------------
···1 字节········1 字节·······3 字节········1 字节·····1 字节·· ·0-250 字节
-----------------------------------------------------------------------

  • 元素 ID:元素 ID 字段可用于指示特定于供应商的元素。
  • 长度:长度是组织标识符、类型、版本和正文的总长度。
  • 组织标识符:组织标识符包含一个唯一标识符 (比如 0x18fe34),为乐鑫指定的 MAC 地址的前三个字节。
  • 类型:类型字段设置为 4,代表 ESP-NOW。
  • 版本:版本字段设置为 ESP-NOW 的版本。
  • 正文:正文包含 ESP-NOW 数据。

广播

本案例同时作为发送端和接受端
发送端:广播数据hello espnow
接收端:接收所有的espnow广播数据

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "esp_system.h"
#include "esp_wifi.h"
#include "espnow.h"
#include "espnow_storage.h"
#include "espnow_utils.h"
#include "esp_mac.h"
#include "event_bus.h"

static const char *TAG = "module_espnow_multi_hop";

#define ESPNOW_TX 1

static void espnow_send_task(void *arg) {
    const char *data = "hello espnow";
    size_t size = strlen((const char *) data);
    espnow_frame_head_t frame_head = {
            .retransmit_count = 5,
            .broadcast        = true,
    };
    for (;;) {
        espnow_send(ESPNOW_DATA_TYPE_DATA, ESPNOW_ADDR_BROADCAST, data, size, &frame_head, pdTICKS_TO_MS(500));
        vTaskDelay(pdMS_TO_TICKS(1000));
    }
}


static esp_err_t espnow_recv_handle(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);

    static uint32_t count = 0;

    ESP_LOGI(TAG, "espnow_recv, <%" PRIu32 "> [" MACSTR "][%d][%d][%u]: %.*s",
             count++, MAC2STR(src_addr), rx_ctrl->channel, rx_ctrl->rssi, size, size, (char *) data);

    uint8_t rssi[1] = {0};
    rssi[0] = (uint8_t)(rx_ctrl->rssi*(-1));
    ESP_LOGI(TAG, "rx_ctrl->rssi: %d", rx_ctrl->rssi);
    ESP_LOGI(TAG, "rssi: %d", rssi[0]);
    event_bus_publish(EVENT_ESPNOW_NOTIFY, rssi, 1, 0);
    return ESP_OK;
}

void module_espnow_multi_hop_init() {
    espnow_config_t espnow_config = ESPNOW_INIT_CONFIG_DEFAULT();
    espnow_init(&espnow_config);
#if ESPNOW_TX
    xTaskCreate(espnow_send_task, "espnow_send_task", 4096, NULL, 10, NULL);
#endif
    espnow_set_config_for_data_type(ESPNOW_DATA_TYPE_DATA, true, espnow_recv_handle);
}

最小Mesh 方案

  • 发起者(Leader)广播 Mesh 消息
  • 接收者保存发起者 ID 即可入网
  • 接收者执行:
    • 校验消息发起者 ID 是否属于自己的网络(1. 引入 随机转发延时 (5~50ms),避免节点同时转发导致冲突,在延迟期间如果听到了相同 seq 被其他节点转发,则自己取消转发 2. 当自己经常收到2条以上消息的时候,自己就不执行转发功能)
    • 校验 Seq 是否已处理过,避免重复处理(维护一个Seq队列)
    • 校验 TTL,限制网络深度
    • 符合条件则转发,实现多跳
    • 详细代码见Gitea
ESP
License:  CC BY 4.0
Share

Further Reading

OLDER

(Elec)来复再生式晶体管单管收音机

NEWER

(Linux)Arduino UNO Q 配置汇总

Recently Updated

  • (ESP-IDF)LVGL 模拟器
  • (ESP-IDF)LVGL 自定义对象加入编码器组
  • (ESP-IDF)vscode配置文件
  • (Elec)来复再生式晶体管单管收音机
  • (ESP-IDF)ESPNOW

Trending Tags

LVGL WCH Linux Elec ThatProject freeRTOS STM ESP Flutter Others

Contents

©2026 松果工作室. Some rights reserved.

Using the Halo theme Chirpy