基于ESP8266 WiFi嗅探的低成本人员流量统计方案
2026/8/20 2:47:05 网站建设 项目流程

1. 项目概述:用ESP8266打造一个“会数数”的WiFi热点

几年前,我在一个小型线下活动帮忙,主办方想知道每个时段有多少人进出主会场,好调整茶歇和活动节奏。当时用的是最土的办法——派两个人在门口手动计数,结果可想而知,不是漏了就是重复,数据一团糟。从那时起,我就琢磨着,能不能用手里玩剩下的ESP8266开发板,做个低成本、自动化的“电子眼”来数人头?这个想法,就是今天要聊的“ESP8266 WiFi People Counter”的起点。

简单说,这是一个利用ESP8266单片机的WiFi信号探测能力,来统计特定区域内人员数量的装置。它不依赖摄像头,不涉及复杂的图像识别,而是通过监听和分析周围设备(主要是手机)发出的WiFi探测请求帧,来估算人数。听起来有点玄乎?其实原理很直接:现代人的手机,只要WiFi功能开着(即使没连上任何网络),就会周期性地广播一种名为“Probe Request”的帧,寻找曾经连接过的WiFi网络。我们的ESP8266,就像一个安静的监听者,捕捉这些广播,并去重计数。

这个项目能解决什么问题呢?首先是低成本的人员流量统计。无论是小店、展厅、图书馆的安静区域,还是家庭办公室,你都可以用它来了解空间的使用情况,又不用承担高昂的商业客流统计系统费用。其次,它保护隐私。与摄像头方案不同,我们获取的只是设备的MAC地址(一个唯一的硬件标识符),不涉及人脸等生物信息。最后,它极具可玩性和扩展性。数据可以通过WiFi上传到服务器、显示在本地屏幕,甚至触发其他智能设备(比如人多了自动开空调)。

适合谁来玩?如果你对物联网、单片机编程(特别是Arduino生态)有初步了解,或者是个喜欢动手解决实际问题的创客,那么这个项目会非常对胃口。即使你是新手,只要跟着步骤走,也能一步步实现。整个过程,你会接触到网络嗅探、数据过滤、MQTT通信、Web服务器搭建等多个实用技能点,绝对干货满满。

2. 核心原理与方案选型:为什么是ESP8266和WiFi嗅探?

2.1 技术原理深度拆解

这个项目的核心,在于理解“WiFi探测请求”。当你的手机WiFi开启时,它为了能快速连接到已知网络(比如家里的、公司的),会不断地在后台“喊话”:“XX咖啡馆的WiFi在吗?”“我家的路由器在吗?”。这种“喊话”就是Probe Request帧。每个帧里都包含了发送设备的MAC地址。

ESP8266有一个非常强大的功能,就是可以将其WiFi芯片设置为“监听模式”。在这个模式下,它不再作为一个普通的WiFi客户端或接入点,而是变成一个被动的无线电接收器,能够捕获空中所有它所能听到的WiFi数据包,包括这些Probe Request。

我们的计数逻辑就建立在捕获这些包的基础上:

  1. 捕获:ESP8266持续监听空中的WiFi数据包。
  2. 过滤:只提取类型为“管理帧”且子类型为“Probe Request”的包。
  3. 提取:从这些包中解析出源MAC地址。
  4. 去重与计数:我们将MAC地址加入一个列表中,并设置一个“观察窗口”(比如60秒)。如果一个MAC地址在窗口内首次出现,我们就认为发现了一个新设备(可能对应一个人)。如果一个已知的MAC地址超过窗口时间没有再出现,我们就认为该设备已离开,并从列表中移除。
  5. 统计:当前列表中的唯一MAC地址数量,就是估算的在场人数。

注意:这里存在一个关键假设——“一人一设备”。虽然不绝对(有人可能带多台设备,也有人可能关闭WiFi),但对于大多数公共场景的流量趋势分析,这个估算具有很高的参考价值。我们的目标是观察相对变化和趋势,而非绝对精确的个位数。

2.2 为什么选择ESP8266而不是其他方案?

市面上能做人数统计的方案很多,为什么独独推荐ESP8266这套方案呢?我们来做个对比:

方案成本隐私性复杂度适用场景精准度
ESP8266 WiFi嗅探极低 (约20元)高 (仅MAC地址)中等趋势分析、流量监控中等 (设备级估算)
红外对射传感器极高 (无个人信息)门口单向计数高 (但需安装对齐)
摄像头+AI识别低 (涉及人脸)安防、精准客流
商用客流统计器非常高中/高 (取决于型号)商场、店铺

选择ESP8266的核心理由:

  1. 成本与性能的完美平衡:一块NodeMCU开发板不到一杯咖啡的钱,却集成了WiFi、MCU和足够的内存,性能足以胜任此任务。
  2. 非侵入式部署:无需像红外对射那样精确对准安装位置,也无需像摄像头那样引人注目。它可以被巧妙地隐藏在任何角落。
  3. 强大的生态支持:Arduino Core for ESP8266和丰富的库函数,使得开发监听模式、处理网络包变得相对简单。
  4. 数据可集成性:计数结果可以轻松通过WiFi发送到MQTT服务器、Web接口或本地显示屏,无缝融入现有的智能家居或物联网系统。

2.3 硬件选型与清单

你不需要准备太多东西,以下是核心清单:

  1. ESP8266开发板:推荐NodeMCU或Wemos D1 Mini。它们自带USB转串口芯片,方便烧录和调试。
  2. USB数据线:用于供电和程序烧录。
  3. 电源:如果长期部署,需要一个5V/1A的USB电源适配器。
  4. (可选)显示屏:如OLED SSD1306 (I2C接口),用于本地实时显示人数。
  5. (可选)外壳:3D打印或找一个合适的小盒子,让项目更美观。

这里有个实操心得:在购买ESP8266时,注意一下芯片的具体型号。ESP-12E/F系列是主流,内存更大更稳定。对于这个项目,任何一款支持Arduino且能运行监听模式的ESP8266板子都可以。

3. 开发环境搭建与核心代码解析

3.1 软件环境准备

我们将在Arduino IDE中进行开发。首先需要做好基础配置:

  1. 安装Arduino IDE:从官网下载并安装最新版。
  2. 添加ESP8266开发板支持
    • 打开Arduino IDE,进入“文件”->“首选项”。
    • 在“附加开发板管理器网址”中填入:http://arduino.esp8266.com/stable/package_esp8266com_index.json
    • 点击“确定”,然后进入“工具”->“开发板”->“开发板管理器”。
    • 搜索“esp8266”,找到并安装“esp8266 by ESP8266 Community”。
  3. 安装必要的库:我们主要依赖两个库:
    • ESP8266WiFi:这是核心,已经包含在开发板支持包里,用于设置监听模式。
    • ArduinoJson(可选,用于数据上传):通过库管理器搜索安装。
    • SSD1306SH1106(可选,用于OLED显示):通过库管理器搜索“Adafruit SSD1306”和“Adafruit GFX”并安装。

3.2 核心代码结构与解析

下面我将分块解析代码的关键部分。完整的代码会整合在最后。

第一部分:全局变量与设置

#include <ESP8266WiFi.h> #include <list> #include <map> // 设置监听的信道(1-13),通常设为所有信道循环扫描,或固定到目标设备常用信道 #define CHANNEL 1 #define MAX_DEVICES 100 // 最大跟踪设备数,防止内存溢出 #define SCAN_INTERVAL 1000 // 主循环扫描间隔(毫秒) #define DEVICE_TIMEOUT 60000 // 设备超时时间(毫秒),例如60秒 // 用于存储设备信息的结构体 struct Device { String mac; unsigned long lastSeen; }; std::list<Device> deviceList; // 使用链表存储当前在场的设备 unsigned long lastChannelHop = 0; int currentChannel = CHANNEL; // 嗅探回调函数原型 void sniffer_callback(uint8_t *buf, uint16_t len);

这里我们使用std::list来动态管理设备列表。选择链表是因为我们需要频繁地进行插入和删除操作(设备加入和超时移除),链表的效率更高。DEVICE_TIMEOUT是关键参数,它定义了“离开”的判断标准。设置太短,人会“被离开”得太快;设置太长,统计结果会滞后。需要根据实际场景调整。

第二部分:初始化与监听模式设置

void setup() { Serial.begin(115200); Serial.println("\n[+] ESP8266 WiFi People Counter Starting..."); // 将WiFi设置为混杂模式(监听模式) wifi_set_opmode(STATION_MODE); wifi_promiscuous_enable(0); // 先禁用 WiFi.disconnect(); // 设置嗅探回调函数 wifi_set_promiscuous_rx_cb(sniffer_callback); wifi_promiscuous_enable(1); // 启用混杂模式 Serial.println("[+] Sniffer started. Listening for probe requests..."); }

wifi_set_opmode(STATION_MODE)WiFi.disconnect()确保ESP8266不连接任何网络,专心做监听。wifi_set_promiscuous_rx_cb是核心,它注册了一个回调函数。此后,每收到一个WiFi数据包,这个回调函数都会被自动调用。

第三部分:数据包嗅探与解析(核心中的核心)

void sniffer_callback(uint8_t *buf, uint16_t len) { // 确保数据包足够长,包含MAC头 if (len < 28) return; // 解析数据包类型(字节12的低4位) uint8_t typeSubtype = buf[12]; uint8_t type = (typeSubtype >> 2) & 0x03; // 帧类型 uint8_t subtype = (typeSubtype >> 4) & 0x0F; // 帧子类型 // 只处理“管理帧”(Type=0)中的“探测请求”(Subtype=4) if (type != 0 || subtype != 4) return; // 提取源MAC地址(位于数据包偏移22字节处) char macAddr[18]; snprintf(macAddr, sizeof(macAddr), "%02X:%02X:%02X:%02X:%02X:%02X", buf[22], buf[23], buf[24], buf[25], buf[26], buf[27]); // 更新或添加设备到列表 updateDeviceList(String(macAddr)); }

这段代码是项目的引擎。它像流水线上的质检员,快速判断每个数据包是不是我们想要的“Probe Request”。buf[12]这个位置存放着帧的控制信息,我们通过位运算提取出类型和子类型。MAC地址在数据包中有固定的位置(这里是第22到27字节),我们将其格式化成常见的“XX:XX:XX:XX:XX:XX”形式。

重要提示:数据包结构是基于IEEE 802.11标准的。不同的库或底层驱动,偏移量可能微有差异。如果收不到设备,可以尝试将偏移量22微调(如试一下26)。这是调试过程中可能遇到的第一个坑。

第四部分:设备列表管理与去重逻辑

void updateDeviceList(String mac) { unsigned long currentTime = millis(); // 1. 查找设备是否已在列表中 for (auto &dev : deviceList) { if (dev.mac == mac) { dev.lastSeen = currentTime; // 更新最后出现时间 return; // 已存在,直接返回 } } // 2. 如果不在列表中,且列表未满,则添加为新设备 if (deviceList.size() < MAX_DEVICES) { Device newDev; newDev.mac = mac; newDev.lastSeen = currentTime; deviceList.push_back(newDev); Serial.printf("[+] New device detected: %s. Total: %d\n", mac.c_str(), deviceList.size()); } else { Serial.println("[!] Device list full!"); } } void cleanupDeviceList() { unsigned long currentTime = millis(); // 使用迭代器安全地移除超时设备 for (auto it = deviceList.begin(); it != deviceList.end(); ) { if (currentTime - it->lastSeen > DEVICE_TIMEOUT) { Serial.printf("[-] Device removed (timeout): %s\n", it->mac.c_str()); it = deviceList.erase(it); // erase返回下一个有效迭代器 } else { ++it; } } }

updateDeviceList实现了去重和添加。cleanupDeviceList则定期清理“僵尸”设备。这里使用millis()记录时间,避免了delay()带来的阻塞问题,让系统能持续响应。注意链表遍历中删除元素的操作,it = deviceList.erase(it)是标准的安全写法。

第五部分:主循环与信道跳变

void loop() { unsigned long currentMillis = millis(); // 定期清理超时设备 cleanupDeviceList(); // 可选:信道跳变以捕获更多设备(有些设备可能固定在某些信道发送探测请求) if (currentMillis - lastChannelHop > 500) { // 每500ms跳一次信道 lastChannelHop = currentMillis; currentChannel = (currentChannel % 13) + 1; // 在1-13信道间循环 wifi_set_channel(currentChannel); } // 这里可以添加数据上报、显示刷新等逻辑 // 例如:每5秒通过串口输出一次当前人数 static unsigned long lastReport = 0; if (currentMillis - lastReport > 5000) { lastReport = currentMillis; Serial.printf("[*] Current estimated people count: %d\n", deviceList.size()); } delay(SCAN_INTERVAL); }

信道跳变是一个高级技巧。WiFi有多个信道,设备可能在特定信道上发送探测请求。循环跳变能提高捕获概率。但跳变太快也可能错过数据包,500ms是一个经验值。主循环的核心就是维持“监听-清理-报告”这个流程。

4. 功能扩展与数据可视化

基础计数功能实现后,我们可以让数据“活”起来,这里介绍两种最实用的扩展方向。

4.1 本地OLED显示屏输出

添加一个I2C接口的OLED屏,可以实时显示人数,无需连接电脑查看串口。接线非常简单:ESP8266的D1接屏的SCL,D2接SDA,再连接VCC和GND。

代码上,需要引入Wire.hAdafruit_SSD1306.h库。在setup()中初始化屏幕,然后在主循环中定期刷新显示。

// 在loop()的报告部分替换串口输出为屏幕显示 display.clearDisplay(); display.setCursor(0,0); display.printf("People: %d", deviceList.size()); display.display();

实测下来,0.96寸的OLED屏显示效果非常清晰,适合放在前台或入口处做一个迷你看板。

4.2 通过MQTT上传数据至Home Assistant或服务器

这是将项目融入智能家居或进行远程监控的关键。MQTT是一种轻量级的物联网消息协议。

  1. 搭建MQTT Broker:可以使用本地的Mosquitto,或者云服务如EMQX Cloud。
  2. 安装PubSubClient库:在Arduino库管理中搜索并安装。
  3. 修改代码
    #include <PubSubClient.h> WiFiClient espClient; PubSubClient client(espClient); // 配置MQTT服务器地址、端口、用户名、密码 const char* mqtt_server = "your_broker_ip"; ... void reconnectMQTT() { while (!client.connected()) { if (client.connect("ESP8266PeopleCounter")) { client.publish("home/peoplecounter/status", "online"); } else { delay(5000); } } } // 在loop()中维护连接并定时发布数据 if (!client.connected()) { reconnectMQTT(); } client.loop(); if (currentMillis - lastMqttReport > 10000) { // 每10秒上报一次 char msg[50]; sprintf(msg, "%d", deviceList.size()); client.publish("home/peoplecounter/count", msg); lastMqttReport = currentMillis; }
  4. 在Home Assistant中集成:在configuration.yaml中添加一个MQTT传感器,订阅home/peoplecounter/count主题,就可以在HA仪表盘上看到实时人数,并设置自动化(如人数大于5自动打开客厅主灯)。

这个扩展让项目从一个独立玩具变成了智能家居系统的一个感知节点,价值大大提升。

5. 部署优化与实战避坑指南

5.1 天线与部署位置的选择

ESP8266的信号强度直接影响探测范围。板载PCB天线通常足够覆盖一个标准房间(约50-80平米)。如果想覆盖更大区域,可以考虑外接一根2.4GHz全向天线

部署位置至关重要:

  • 最佳高度:1.5米至2.5米。这个高度避开了大部分地面障碍,也与人们携带手机的高度大致相当。
  • 避开干扰源:远离微波炉、蓝牙音箱、大型金属物体等强烈的2.4GHz干扰源。
  • 中心 vs 入口:如果统计整个区域人数,尽量放在区域中心。如果只统计进出,可以放在门口,但要注意调整DEVICE_TIMEOUT,避免人站在门口时被反复计数。
  • 实测技巧:部署后,用自己的手机开关飞行模式,观察串口日志,看设备是否能被稳定捕获和移除。这是最直接的验证方法。

5.2 参数调优与准确性提升

默认参数不一定适合所有场景,需要微调:

  1. DEVICE_TIMEOUT(设备超时时间)

    • 快餐店/展厅(人流快):建议30-45秒。人停留时间短,需要快速移除。
    • 办公室/图书馆(人流慢):建议90-120秒。避免人坐着不动一会儿就被认为离开。
    • 调试方法:先设一个较长时间(如120秒),观察一个人从进入、静止到离开的完整日志,根据“离开”信号的延迟来调整。
  2. RSSI过滤(信号强度过滤): 在sniffer_callback函数中,可以获取数据包的RSSI值(信号强度指示)。通过只处理RSSI大于某个阈值(如-70dBm)的数据包,可以限定探测范围,避免统计到隔壁房间或路过的人。

    int rssi = buf[1] - 256; // 一种常见的RSSI计算方式 if (rssi < -70) return; // 忽略信号过弱的设备

    这个技巧能显著提升在密集环境(如公寓楼)下的统计准确性。

  3. MAC地址随机化带来的挑战: 现代操作系统(iOS 8+, Android 6+)为了隐私,在发送探测请求时会使用随机的MAC地址。这意味着同一台手机,在不同时间或不同地点,其探测请求中的MAC地址是变化的。这是本项目最大的误差来源。

    • 应对策略
      • 接受趋势,而非绝对数:这是最重要的心态调整。项目依然能有效反映“人变多了”、“人变少了”、“高峰期在几点”等趋势信息。
      • 关注设备OUI:MAC地址的前6位是厂商标识(OUI)。虽然地址随机化,但OUI通常不变(还是苹果、三星等)。可以粗略按OUI分类,但无法精确到台数。
      • 结合其他传感器:对于需要更高精度的场景,可以结合被动式红外(PIR)传感器做辅助判断。

5.3 常见问题排查速查表

问题现象可能原因排查步骤与解决方案
串口无任何输出/不启动接线错误、电源不足、板子型号选错1. 检查USB线是否可传输数据。2. 尝试换一个USB口或电源。3. 在Arduino IDE中确认开发板型号和端口选择正确。
能启动,但检测不到任何设备WiFi未进入监听模式、信道不对、代码偏移量错误1. 检查setup()中监听模式设置代码。2. 关闭手机4G,确保WiFi开启。3. 尝试注释掉信道跳变,固定信道为1、6、11试试。4. 调整sniffer_callback中MAC地址的偏移量(尝试22或26)。
计数数字剧烈跳动或虚高未做去重、DEVICE_TIMEOUT太短、探测到大量无关设备1. 检查updateDeviceList中的去重逻辑。2. 增大DEVICE_TIMEOUT。3. 增加RSSI过滤,屏蔽远处弱信号设备。
设备离开后很久才从计数中消失DEVICE_TIMEOUT设置过长根据场景调小DEVICE_TIMEOUT参数。
MQTT无法连接网络问题、Broker地址/密码错误、防火墙1. 确保ESP8266能连接WiFi。2. 检查MQTT Broker地址、端口、用户名、密码。3. 检查Broker服务是否运行,防火墙是否放行1883端口。
OLED屏不显示I2C地址不对、接线错误、库未安装1. 用I2C扫描程序确认OLED屏的地址(通常是0x3C或0x3D)。2. 检查SDA、SCL是否接反。3. 确认已安装Adafruit SSD1306和GFX库。

5.4 电源管理与长期运行

如果打算7x24小时运行,电源稳定性很重要:

  • 不要依赖电脑USB口:使用独立的5V/1A USB电源适配器。
  • 注意发热:ESP8266在持续工作时会微热,确保部署环境通风,避免密闭空间。
  • 看门狗与异常重启:可以在代码中加入软件看门狗,或者利用ESP8266的硬件看门狗,防止程序跑飞。
    ESP.wdtDisable(); // 谨慎使用,仅用于调试 // 在主循环中定期喂狗 ESP.wdtFeed();
  • 日志与监控:将计数数据定期上报到服务器,不仅可以可视化,还能在数据长时间不变时触发警报,提示设备可能离线或异常。

走完整个流程,从原理理解、环境搭建、代码编写、功能扩展,到最后的部署调优,你应该已经拥有了一个完全属于自己的、低成本的WiFi人流计数器。它可能不是百分百精确,但其提供的趋势数据对于很多非商业的洞察需求已经绰绰有余。更重要的是,这个过程让你深入理解了无线网络通信的一个侧面,以及如何将简单的硬件转化为一个有用的数据感知终端。我自己的那个计数器已经在小工作室里默默运行了半年多,帮我优化了好几次会议室的预约安排,这就是动手创造的乐趣所在。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询