当前位置: 首页 > news >正文

用 ESP8266 RTOS SDK写一个详细的示例,要求只输出beacon包,其它包不处理

下面给出一个基于 ESP8266 RTOS SDK 的完整示例,演示如何在 Promiscuous 模式下只抓取并打印 Beacon 管理帧(802.11 Type=0, Subtype=8),忽略其他所有帧。代码中包含详细注释,帮助你理解每一步的作用。


一、工程准备

  1. 环境要求

    • ESP8266 RTOS SDK(本文示例基于 v3.x/4.x 系列均可)。
    • 已正确配置并能够编译、烧录 ESP8266 的开发环境(如使用 ESP-IDF 工具链或 VSCode + PlatformIO 中的 ESP8266 RTOS SDK 配置)。
    • 板子与电脑已通过串口线连接。
  2. 工程文件结构(假设使用 ESP-IDF 工具链)

    your_project/
    ├── CMakeLists.txt
    ├── sdkconfig                # 根据需要打开的功能自行配置
    └── main/├── CMakeLists.txt└── main.c
    
    • CMakeLists.txtsdkconfig 等文件按常规 ESP-IDF 工程生成即可,不在此赘述。
    • 下面重点展示 main/main.c 的完整内容。

二、main.c 详细代码

#include <string.h>
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "esp_wifi.h"
#include "esp_event.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "tcpip_adapter.h"  // ESP8266 RTOS SDK 中使用 tcpip_adapter 而非 esp_netif// 日志标签,用于 ESP_LOG 输出时区分模块
static const char *TAG = "wifi_sniffer";/*** @brief 抓包回调函数:只处理 Management 帧中的 Beacon 子类型** @param buf  指向一个 wifi_promiscuous_pkt_t 结构体的指针,包含 rx_ctrl(元信息) + payload(帧内容)* @param type 表示这帧的类型:WIFI_PKT_MGMT / WIFI_PKT_CTRL / WIFI_PKT_DATA / WIFI_PKT_MISC*/
static void wifi_sniffer_packet_handler(void *buf, wifi_promiscuous_pkt_type_t type)
{// 1. 只关注管理帧 (Management Frames)if (type != WIFI_PKT_MGMT) {return;}// 2. 将 buf 强制转换为 wifi_promiscuous_pkt_t 结构体const wifi_promiscuous_pkt_t *ppkt = (wifi_promiscuous_pkt_t *)buf;const wifi_pkt_rx_ctrl_t *rx_ctrl = &ppkt->rx_ctrl;   // 收到帧的元信息 (RSSI、信道等)const uint8_t *payload = ppkt->payload;                // 真正的 802.11 帧首字节// 3. 解析 802.11 帧头 (Frame Control 字段) 判断帧子类型//    Frame Control (2 bytes):低字节在 payload[0],高字节在 payload[1]uint16_t frame_ctrl = ((uint16_t)payload[1] << 8) | payload[0];uint8_t frame_type   = (frame_ctrl >> 2) & 0x3;   // 2-bit:00=Management, 01=Control, 10=Datauint8_t frame_subtype= (frame_ctrl >> 4) & 0xF;   // 4-bit 子类型(Beacon=8, Probe Req=4, Probe Resp=5, ...)// 冗余判断:type==WIFI_PKT_MGMT 一般对应 frame_type==0,但额外检查以防意外情况if (frame_type != 0 || frame_subtype != 8) {// 不是 Beacon 管理帧,直接返回return;}// 4. Beacon 帧解析://    - RSSI:rx_ctrl->rssi(dBm,带符号)//    - 抓到的频道:rx_ctrl->channel//    - 源 MAC:前 24 字节 MAC 头部中的 Addr2(Source Address),在 payload[10]~payload[15]//    - SSID:Beacon 的 Tag 信息中第一项通常是 SSID Tag (Tag Number = 0),//      Tag 的起始偏移 = 24 (MAC header) + 12 (beacon 固定字段) = 36int8_t rssi = rx_ctrl->rssi;uint8_t channel = rx_ctrl->channel;// 源 MAC 地址char src_mac_str[18];const uint8_t *mac_src = payload + 10; // Management Frame Header 中的 Address2snprintf(src_mac_str, sizeof(src_mac_str),"%02X:%02X:%02X:%02X:%02X:%02X",mac_src[0], mac_src[1], mac_src[2],mac_src[3], mac_src[4], mac_src[5]);// SSID 字段解析:从 payload[36] 开始const uint8_t *ie_ptr = payload + 36;uint8_t tag_number = ie_ptr[0];uint8_t tag_len    = ie_ptr[1];char ssid[33];  // 802.11 中 SSID 最长 32 字节if (tag_number == 0 && tag_len > 0) {if (tag_len > 32) tag_len = 32;  // 防止越界memcpy(ssid, ie_ptr + 2, tag_len);ssid[tag_len] = '\0';} else {// 如果第一个 Tag 不是 SSID,可视为隐藏网络或不规范,此处直接置空ssid[0] = '\0';}// 5. 最终将信息打印到串口ESP_LOGI(TAG,"Beacon >>> SSID: \"%s\", MAC: %s, RSSI: %d dBm, Channel: %d",ssid, src_mac_str, rssi, channel);
}/*** @brief 应用程序入口函数*/
void app_main(void)
{esp_err_t ret;// -------------------------------// 1. 初始化 NVS -- ESP8266 WiFi 需要底层使用 NVS// -------------------------------ret = nvs_flash_init();if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {// 如果 NVS 分区被占满,先擦除后再初始化ESP_ERROR_CHECK(nvs_flash_erase());ret = nvs_flash_init();}ESP_ERROR_CHECK(ret);// -------------------------------// 2. 初始化 TCP/IP 适配层 + 事件循环// -------------------------------tcpip_adapter_init();ESP_ERROR_CHECK(esp_event_loop_create_default());// -------------------------------// 3. 初始化 WiFi Driver(使用默认配置)// -------------------------------wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();ESP_ERROR_CHECK(esp_wifi_init(&cfg));// -------------------------------// 4. 设置 WiFi Work Mode 为 NULL 模式(既不 Station 也不 SoftAP)//    这样可以仅将 WiFi 射频打开用于 Promiscuous 抓包//    如想先以 STA 模式关联 AP 再抓包,也可以改为 WIFI_MODE_STA// -------------------------------ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_NULL));// WiFi 参数存储只使用 RAMESP_ERROR_CHECK(esp_wifi_set_storage(WIFI_STORAGE_RAM));// -------------------------------// 5. 启动 WiFi Driver// -------------------------------ESP_ERROR_CHECK(esp_wifi_start());// -------------------------------// 6. 配置 Promiscuous Filter:只让 Management 帧通过回调////    WIFI_PROMIS_FILTER_MASK_MGMT 表示只接收管理帧(所有子类型),//    具体在回调里再额外判断是否 Subtype == 8(Beacon)// -------------------------------wifi_promiscuous_filter_t filter = {.filter_mask = WIFI_PROMIS_FILTER_MASK_MGMT};ESP_ERROR_CHECK(esp_wifi_set_promiscuous_filter(&filter));// -------------------------------// 7. 注册回调函数// -------------------------------ESP_ERROR_CHECK(esp_wifi_set_promiscuous_rx_cb(wifi_sniffer_packet_handler));// -------------------------------// 8. 启用 Promiscuous 模式//    如果第二个参数设为 true,则会进入被动抓包,并接收所有 Management/Data/Control/Misc,//    结合上面 filter,可以减少回调次数。(filter 已经只让 MGMT 帧触发)// -------------------------------ESP_ERROR_CHECK(esp_wifi_set_promiscuous(true));// -------------------------------// 9. 设置要抓取的信道 (1~13)//    这里直接锁定在信道 1,后续如果需要轮询信道可以使用定时器动态切换。// -------------------------------uint8_t channel = 1;ESP_ERROR_CHECK(esp_wifi_set_channel(channel, WIFI_SECOND_CHAN_NONE));ESP_LOGI(TAG, "已切换到信道:%d,开始抓取 Beacon 帧...", channel);// -------------------------------// 10. 如果需要轮询信道(可选,可按需打开)//    下面示例演示如何每隔 200ms 切换到下一个信道,从 1~13 循环:// -------------------------------/*static uint8_t current_channel = 1;void channel_hop_timer_cb(void *arg){current_channel = (current_channel % 13) + 1;esp_wifi_set_channel(current_channel, WIFI_SECOND_CHAN_NONE);ESP_LOGI(TAG, "跳转到信道:%d", current_channel);}// 创建并启动 FreeRTOS 定时器TimerHandle_t channel_timer = xTimerCreate("chan_hop",pdMS_TO_TICKS(200),pdTRUE,NULL,channel_hop_timer_cb);xTimerStart(channel_timer, 0);*/// -------------------------------// 11. 到这里 app_main 不再阻塞,Promiscuous 回调会不断输出 Beacon 信息。// -------------------------------
}

三、关键点说明

  1. NVS 初始化
    ESP8266 RTOS SDK 的 WiFi 驱动会调用 NVS 存储来保存一些参数,必须先调用 nvs_flash_init()。如果出现 ESP_ERR_NVS_NO_FREE_PAGES 类似错误,需要先擦除再初始化。

  2. tcpip_adapter_init + esp_event_loop_create_default
    用来初始化底层的 TCP/IP 协议栈及事件循环系统,为后续 esp_wifi_init 等操作做好准备。

  3. esp_wifi_set_mode(WIFI_MODE_NULL)

    • WIFI_MODE_NULL:既不作为 Station,也不作为 SoftAP,仅打开射频,让芯片能工作在 Promiscuous 模式抓包。
    • 如果你想先连上某个 AP 再抓包,也可以改为 WIFI_MODE_STA,但此时可能会抓到自己发出的 Probe/ACK 等帧。使用 NULL 模式能最纯粹地只有“监听”功能。
  4. Promiscuous Filter

    • wifi_promiscuous_filter_t 中的 filter_mask = WIFI_PROMIS_FILTER_MASK_MGMT,表示仅让“管理帧(Management Frames)”触发回调。管理帧包括 Beacon、Probe Request/Response、Association、Authentication 等。
    • 如果把 filter_mask 设为 WIFI_PROMIS_FILTER_MASK_MGMT | WIFI_PROMIS_FILTER_MASK_DATA,则会触发管理帧和数据帧回调。本例里为了降低回调频率,直接只让管理帧进来。
  5. 回调函数 wifi_sniffer_packet_handler

    • 原型是:

      void (* wifi_promiscuous_cb_t)(void *buf, wifi_promiscuous_pkt_type_t type);
      
      • 第一个参数 buf 是一个指向 wifi_promiscuous_pkt_t 的指针,它封装了 RX 控制信息(rx_ctrl)与帧载荷(payload)。
      • 第二个参数 type 告诉你这是管理帧、控制帧、数据帧还是 misc 帧。
    • 本例一开始就判断 type != WIFI_PKT_MGMT,若不是管理帧就直接返回。

    • 再进一步从 payload[0..1](Frame Control)解析出 frame_type(2bit)和 frame_subtype(4bit),只有当 frame_type==0 && frame_subtype==8(即 Beacon)时才继续处理。否则再返回。

    • 对于 Beacon 帧,示例中提取了:

      • RSSIppkt->rx_ctrl.rssi
      • Channelppkt->rx_ctrl.channel
      • Source MACpayload + 10 (Management Frame Header 中的 Address2)
      • SSID:从 payload + 36 开始,第一项 Tag(Tag Number = 0)即为 SSID 参数集。若 Tag Number ≠ 0 或长度为 0,则认为是隐藏 SSID,此处示例直接输出空字符串。
    • 最后通过 ESP_LOGI 将抓到的 Beacon 信息(SSID、MAC、RSSI、Channel)输出到串口。

  6. 信道设置

    • esp_wifi_set_channel(1, WIFI_SECOND_CHAN_NONE):将收包信道固定在 1。你可以根据周围 AP 分布情况自行修改信道;
    • 如果想覆盖整个 1~13 信道,可参考注释部分的定时器示例,每 200ms 切换一次信道。
  7. 打印日志

    • 本示例使用 ESP_LOGI(TAG, ...) 输出日志,你也可改成 printf/ESP_LOGE 等,根据实际需求调整。

    • 编译后,在串口工具中将波特率设置为 115200(或 sdkconfig 中配置的默认波特率),即可看到类似如下输出:

      I (210) wifi_sniffer: 已切换到信道:1,开始抓取 Beacon 帧...
      I (510) wifi_sniffer: Beacon >>> SSID: "MyWiFi", MAC: 12:34:56:78:9A:BC, RSSI: -45 dBm, Channel: 1
      I (1210) wifi_sniffer: Beacon >>> SSID: "GuestNet", MAC: DE:AD:BE:EF:00:11, RSSI: -60 dBm, Channel: 1
      ...
      

四、注意事项

  1. 并发性能

    • Promiscuous 模式下回调非常频繁,尤其在信道人多、AP 较多环境下。如果回调函数中做过多耗时操作会导致丢包。
    • 本示例中仅完成最简单的字符串拼接与 ESP_LOGI,已经尽可能减少阻塞;若后续要把抓到的原始帧丢给其他任务进一步处理,建议使用 FreeRTOS 队列或环形缓冲,将 payload 数据先复制到独立缓冲,然后由专门任务做深度解析。
  2. 信道跳频 (Channel Hopping)

    • 如果只固定在一个信道(如 1),只能抓到该信道上发射的 Beacon(以及 Probe Response、某些广播)。
    • 若要覆盖所有常见信道,可使用定时器 (xTimerCreate) 每隔几百毫秒调用 esp_wifi_set_channel()
    • 注意:在跳频期间,如果切换过快,Promiscuous 回调几乎“看不到”任何帧;如果切换过慢,虽然能多抓到 Beacon,但响应速度变慢。一般建议每 200~500ms 跳一个信道。
  3. 隐藏 SSID 与私有扩展

    • 如果某 AP 隐藏了 SSID(SSID 长度为 0),本例将不会输出 SSID 内容。你可以自行改造:当 tag_number!=0 时,继续遍历后续 Tag,直到找到 Tag Number = 0 或结束。
    • Beacon 中可能包含多种 Information Element (IE),如 HT Capabilities、Vendor Specific 等,应用层可根据需要自行解析。
  4. 功耗与天线

    • Promiscuous 模式下 WiFi 射频始终处于监听状态,功耗高于普通 Station/SoftAP 模式。
    • 板子天线设计、外部环境(墙壁、干扰信号)也会影响抓包效果。
    • 在实际项目中,可根据使用场景决定是否在特定时段才开启抓包,其他时间切回普通模式。

在这里插入图片描述

五、小结

以上示例演示了如何使用 ESP8266 RTOS SDK 进入 WiFi Promiscuous 模式,只捕获并打印 Beacon 帧,忽略其他所有 Management/Data/Control 帧。你可以根据业务需要对回调函数和信道策略进行二次开发,例如:

  • 进一步解析 Beacon 里携带的更多 IE 信息;
  • 将抓到的 Beacon 信息通过 UDP/HTTP 上报到云端;
  • 或在捕获到特定 SSID/特定 MAC 时触发其他动作。

只需根据示例中的步骤(NVS → TCPIP → WiFi 初始化 → 配置 Filter → 注册回调 → 启用 Promiscuous → 切信道),即可快速搭建一个只输出 Beacon 的 WiFi Sniffer 模块。祝你开发顺利!

http://www.lqws.cn/news/152515.html

相关文章:

  • 【Go语言基础【5】】运算符基础
  • USB-C/HDMI 2.0 2:1 SW,支持4K60HZ
  • 全面解析网络端口:概念、分类与安全应用
  • Hive SQL常见操作
  • 邮件分类项目
  • 【汇编逆向系列】六、函数调用包含多个参数之多个整型-参数压栈顺序,rcx,rdx,r8,r9寄存器
  • 【安全攻防与漏洞】​​量子计算对HTTPS的威胁:后量子密码学进展
  • 量子计算+AI:特征选择与神经网络优化创新应用
  • 新版NANO下载烧录过程
  • 007-nlohmann/json 项目应用-C++开源库108杰
  • LFWG2024.08
  • 互联网大厂Java面试:从Spring Cloud到Kafka的技术考察
  • 如何给windos11 扩大C盘容量
  • golang 如何定义一种能够与自身类型值进行比较的Interface
  • 【Go语言基础】基本语法
  • 命令行运行python程序报错 ImportError: /lib/x86_64-linux-gnu/libstdc++.so.6
  • Vite模块联邦(vite-plugin-federation)实现去中心化微前端后台管理系统架构
  • 《C++初阶之类和对象》【命名空间 + 输入输出 + 缺省参数 + 函数重载】
  • ubuntu系统文件误删(/lib/x86_64-linux-gnu/libc.so.6)修复方案 [成功解决]
  • .Net Framework 4/C# LINQ*
  • 机器学习的数学基础:决策树
  • 双空间知识蒸馏用于大语言模型
  • win32相关(远程线程和远程线程注入)
  • 初探Service服务发现机制
  • 基础线性代数
  • 文档处理组件Aspose.Words 25.5全新发布 :六大新功能与性能深度优化
  • Python实现markdown文件转word
  • 【react+antd+vite】优雅的引入svg和阿里巴巴图标
  • Java在word中指定位置插入图片。
  • npm run dev 报错:Error: error:0308010C:digital envelope routines::unsupported