WiFi广播包(WiFi Beacon或Probe Request)是一种更普遍的广播方式,适用于所有支持WiFi的设备。
实现步骤:
- 设置ESP32为WiFi接入点(AP):配置ESP32为WiFi接入点(AP模式),这可以让周围的设备扫描到ESP32的SSID。
- 自定义Beacon包:修改ESP32的WiFi Beacon帧,在其数据字段中嵌入你想广播的自定义信息。
- 周期性发送Beacon帧:让ESP32定期发送包含数据的WiFi Beacon帧,周围的设备无需连接到ESP32的网络,也可以通过扫描获得这些广播数据。
这种方式不需要目标设备专门支持ESP-NOW,所有支持WiFi的设备都可以接收到这些广播数据。
使用WiFi广播包(如Beacon帧或Probe Request帧)在ESP32上发送自定义数据,需要深入操作ESP32的WiFi底层接口。虽然ESP-IDF原生支持对这些帧的操作,但Arduino框架中并没有直接提供修改WiFi Beacon帧的功能。
不过,你可以使用ESP32的ESP-IDF框架来修改WiFi Beacon帧,下面是一个使用ESP-IDF的示例,展示如何自定义并发送WiFi Beacon帧:
示例代码(自定义WiFi Beacon帧 - ESP-IDF):
#include "esp_wifi.h"
#include "esp_event.h"
#include "nvs_flash.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
static const char *TAG = "beacon_broadcast";
// 定义自定义Beacon帧
uint8_t beacon_raw[] = {
0x80, 0x00, // 帧控制字段(Frame Control)
0x00, 0x00, // 持续时间(Duration)
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // 广播地址(Broadcast Address)
0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, // 源地址(Source Address)
0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, // BSSID
0x00, 0x00, // 序列控制字段
0x00, 0x00, // 时间戳
0x64, 0x00, // Beacon间隔(Beacon Interval)
0x31, 0x04, // 能力信息字段
0x00, 0x06, 'E', 'S', 'P', '3', '2', // SSID字段
0x01, 0x08, 0x82, 0x84, 0x8b, 0x96, 0x24, 0x30, 0x48, 0x6c, // 支持的数据速率
0x03, 0x01, 0x01 // 当前通道
};
void send_beacon_task(void *pvParameter) {
while (true) {
esp_wifi_80211_tx(WIFI_IF_AP, beacon_raw, sizeof(beacon_raw), false);
ESP_LOGI(TAG, "Beacon帧已发送");
vTaskDelay(pdMS_TO_TICKS(100)); // 每100ms发送一次
}
}
void app_main(void) {
// 初始化NVS(用于存储WiFi配置)
esp_err_t ret = nvs_flash_init();
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
ESP_ERROR_CHECK(nvs_flash_erase());
ret = nvs_flash_init();
}
ESP_ERROR_CHECK(ret);
// 初始化WiFi
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_AP)); // 设置为AP模式
ESP_ERROR_CHECK(esp_wifi_start());
// 创建发送Beacon帧的任务
xTaskCreate(&send_beacon_task, "send_beacon_task", 4096, NULL, 5, NULL);
}
代码解析:
- 自定义Beacon帧:
beacon_raw
数组中存储了WiFi Beacon帧的数据,你可以根据需要修改SSID和其他字段。 - Beacon发送任务:
send_beacon_task
是一个FreeRTOS任务,它不断调用esp_wifi_80211_tx()
函数来发送自定义的Beacon帧。这里使用WIFI_IF_AP
接口将帧广播出去。 - 初始化WiFi:通过
esp_wifi_init()
和相关配置,将ESP32设置为WiFi接入点(AP模式),以便能够发送Beacon帧。
编译和运行:
- 你需要使用ESP-IDF进行开发,确保已经安装并配置好开发环境。
- 将上述代码放入
main.c
文件中,然后使用idf.py build
和idf.py flash
命令编译并烧录到ESP32中。
注意事项:
- 帧格式:WiFi帧格式必须符合802.11标准,你可以根据需要修改Beacon帧中的字段,如SSID、自定义信息等。
- 频率控制:不要频繁发送Beacon帧,以免影响其他设备的正常通信,通常Beacon帧的发送间隔为100ms左右。
这种方法能让附近的所有支持WiFi的设备通过扫描SSID看到你的自定义信息。
在WiFi Beacon帧中,自定义数据可以放置在帧的**可选信息元素(Information Element, IE)**部分。这些信息元素是灵活的字段,用于传递额外信息。你可以在SSID字段后添加一个自定义的IE来携带自己的数据。
WiFi Beacon帧结构概述:
WiFi Beacon帧的基本结构包含以下部分:
- MAC头:包含帧控制、持续时间、目的地址、源地址、BSSID等信息。
- 固定参数:
- 时间戳
- Beacon间隔
- 能力信息
- 可选信息元素(IEs):这里包括SSID、支持的数据速率、当前通道等。
你可以在这些信息元素之后加入你自己的自定义IE,格式为:
- Tag Number(1字节):标识该IE的类型,取值范围0-255。你可以使用一个未被标准定义使用的数值(如
221
,用于厂商自定义的IE)。 - Tag Length(1字节):自定义数据的长度。
- Tag Value(可变长度):实际的数据内容。
示例:在Beacon帧中插入自定义数据
在前面示例代码的基础上,修改Beacon帧,插入自定义数据。假设你想插入"Hello"这段字符串作为自定义数据:
自定义IE格式:
- Tag Number: 0xDD(221,表示厂商自定义IE)
- Tag Length: 5("Hello"字符串的长度)
- Tag Value: 5字节的"Hello"字符串(0x48 0x65 0x6C 0x6C 0x6F)
修改后的示例代码:
#include "esp_wifi.h"
#include "esp_event.h"
#include "nvs_flash.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
static const char *TAG = "beacon_broadcast";
// 定义自定义Beacon帧,插入自定义数据 "Hello"
uint8_t beacon_raw[] = {
0x80, 0x00, // 帧控制字段(Frame Control)
0x00, 0x00, // 持续时间(Duration)
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // 广播地址(Broadcast Address)
0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, // 源地址(Source Address)
0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, // BSSID
0x00, 0x00, // 序列控制字段
0x00, 0x00, // 时间戳
0x64, 0x00, // Beacon间隔(Beacon Interval)
0x31, 0x04, // 能力信息字段
0x00, 0x06, 'E', 'S', 'P', '3', '2', // SSID字段
0x01, 0x08, 0x82, 0x84, 0x8b, 0x96, 0x24, 0x30, 0x48, 0x6c, // 支持的数据速率
0x03, 0x01, 0x01, // 当前通道
// 自定义信息元素(IE): "Hello"
0xDD, // Tag Number (厂商自定义IE)
0x05, // Tag Length (5字节数据)
0x48, 0x65, 0x6C, 0x6C, 0x6F // 自定义数据 "Hello"
};
void send_beacon_task(void *pvParameter) {
while (true) {
// 发送自定义Beacon帧
esp_wifi_80211_tx(WIFI_IF_AP, beacon_raw, sizeof(beacon_raw), false);
ESP_LOGI(TAG, "Beacon帧已发送");
vTaskDelay(pdMS_TO_TICKS(100)); // 每100ms发送一次
}
}
void app_main(void) {
// 初始化NVS
esp_err_t ret = nvs_flash_init();
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
ESP_ERROR_CHECK(nvs_flash_erase());
ret = nvs_flash_init();
}
ESP_ERROR_CHECK(ret);
// 初始化WiFi
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_AP)); // 设置为AP模式
ESP_ERROR_CHECK(esp_wifi_start());
// 创建发送Beacon帧的任务
xTaskCreate(&send_beacon_task, "send_beacon_task", 4096, NULL, 5, NULL);
}