ESP32-C3 上的 Pi-hole:2 美元打造 DNS 广告拦截器,50KB 内存塞下 53 万域名
花 2 美元买个 ESP32-C3,就能在家里搭一个 Pi-hole 级别的 DNS 广告拦截器,而且不需要外接 PSRAM。最近这个项目在推特上火了一把,被 Tom’s Hardware、XDA 都报道过,GitHub 上已经 3000+ star。
它最绝的地方不是”能拦广告”,而是怎么把 53 万个域名塞进一颗廉价芯片里。

这是什么
esp32-c3-adblock 是一个跑在 $2 ESP32-C3 上的 DNS 广告拦截器(DNS sinkhole),作者是 Mahmoud Abozaid,C++ 写的,MIT 协议开源。
它做的事情和 Pi-hole 一样:把广告 / 追踪域名的 DNS 查询拦下来,直接回 0.0.0.0,让设备根本连不上那些服务器。但它不依赖树莓派、不依赖 Docker,一个小 U 盘大小的板子插在路由器 USB 口上就行。

核心脑洞:域名不进内存,进 flash
大多数 ESP32 上的 DNS 拦截方案,都是把黑名单(域名字符串)整个加载进 RAM,所以必须配 PSRAM,整套下来要 8 美元左右。
这个项目的思路恰恰相反:把域名算成 40 位的 FNV-1a 哈希,按大小排序存进 flash,查询时用二分查找匹配。

查询流程大概是这样:
1 | 查询进来 → 提取域名 → 算 FNV-1a 哈希(带上父域名后缀) |
效果对比:
| 字符串塞 RAM 方案 | 本项目(哈希存 flash) | |
|---|---|---|
| 硬件 | ESP32 + PSRAM(约 $8) | ESP32-C3,无 PSRAM(约 $2) |
| 14.1 万域名 | 约 2.5 MB RAM | 0.67 MB flash |
| 占用 RAM | 一大半 | 约 50 KB |
| 查找方式 | 字符串比较 | 约 18 次 flash 读(含 WiFi 往返约 10 ms) |
| 哈希碰撞 | 无 | 14.1 万时 0 次,53.7 万时约 1 次 |
为什么是 40 位?这是 flash 预算下的甜点值。哈希碰撞遵循生日悖论——14 万域名基本不撞,53.7 万大约撞 1 个(也就是有一个倒霉域名会被误杀)。降到 32 位能省 20% flash 但 25 万时就撞 7 个;升到 64 位又纯属浪费。
值得一提的是,这套”哈希存 flash”的思路在更大的芯片上同样成立:16 MB 的 ESP32-S3 能塞下约 270 万域名,而同样 8 MB PSRAM 用字符串只能放约 46 万。
硬件准备
- 任意 ESP32-C3 开发板(作者实测 C3 SuperMini),4 MB flash,不需要 PSRAM
- 经典 ESP32(DevKit / WROOM,4 MB)也能编译(社区贡献,C3 才是主测目标)
- 供电用稳定的 USB 电源(手机充电头或路由器 USB 口都行),劣质的 USB-C→A 转接头会在 WiFi 发射时把射频拉垮
- 配一个 USB-A → USB-C 转接头,直接插在路由器背面的多余 USB 口上——不用额外电源、不用多一个盒子
还有人做了一个 3D 打印外壳(仓库里 hardware/esp32-c3-supermini-enclosure.stl),打印时注意保持天线那头镂空,别把板载 PCB 天线埋进实心塑料里。
编译与烧录(PlatformIO)
只需要一次 USB 烧录,之后固件和黑名单都能走 WiFi 更新。
⚠️ 用较新的 PlatformIO:VSCode 插件自带的旧 core,或者发行版 apt 装的
platformio(比如 4.3.4)都会报AttributeError失败。建议pip install -U platformio。
1 | # 1. 复制密钥模板(不进 git),按需填写 WEB_USER / WEB_PASS / OTA_PASS(必填) |
黑名单来源很灵活:hosts 文件、纯域名列表、AdGuard / Adblock 基础规则(||ads.example.com^)都支持;@@ 规则用来放行。被拦的域名同时拦掉它的子域名。
Linux 踩坑:Fedora / Ubuntu 默认的 ModemManager 会抢 /dev/ttyACM0 并翻转 DTR/RTS,把 C3 复位、卡住串口。遇到烧录/监视异常先 sudo systemctl stop ModemManager 并加一条 udev 规则忽略该设备。
WiFi 配网不用重烧
如果连不上网(或你压根没填 secrets.h),它会起一个开放热点 C3-AdBlock-XXXX,带 captive portal——手机连上去选网络、输密码就行。以后要换网络,点面板上的 Forget WiFi,或者通电时按住 BOOT 键,配网门户就回来了。
怎么用
把设备的 DNS 指向 C3 的 IP,或者在你的主 DNS 后面把它加为备用解析器。测试一下:
1 | dig @<c3-ip> doubleclick.net # -> 0.0.0.0(被拦) |
免 USB 的 OTA 更新
面板 http://c3adblock.local 全包了:
- 黑名单:直接拖一个
blocklist.bin上传,或者填一个远程地址让它定时拉。项目用 GitHub Actions 每周一重新生成一份默认名单,固定地址贴一次就一直保持更新:https://github.com/M-Abozaid/esp32-c3-adblock/releases/download/blocklist/blocklist.bin - 固件:在 Firmware → OTA update 上传
firmware.bin,设备校验后重启进新固件
⚠️ 4 MB flash 的取舍:固件 OTA 需要两个 app 分区,留给黑名单的空间就只剩约 1.3 MB(最多约 25 万域名)。那个激进的 53.7 万”终极名单”只能用在单 app 分区表下(不支持固件 OTA)。在 partitions.csv 里自己权衡。
安全提醒(认真看)
这东西蹲在你家每条 DNS 查询的路径上,安全不能马虎:
- 面板只读页面(
/、/stats.json)对外开放,但所有改状态的接口(/ban、/addblock、/upload、/update、/setupdate、/forgetwifi等)都要 HTTP Basic Auth(WEB_USER/WEB_PASS) - 网络 OTA 还要单独的
OTA_PASS。别用示例文件里的默认密码——那是公开写在仓库里的,设备会在面板和串口告警,但依然会启动 - 还有一道 CSRF 防线:每个写接口额外要求
X-Requested-With: c3-adblock头,挡掉网页<img>/ 表单自动带凭证的跨站调用
不过要清醒:这一切都是 HTTP 明文(:80),没有 TLS——这片芯片跑不动 HTTPS 服务器。Basic Auth 的密码只是 base64,不是加密,能嗅探你局域网流量的人(开放 WiFi、ARP 欺骗)就能抓到。所以它防范的是”局域网里其他设备空手调 API”和”浏览器 CSRF”这种常见情况,挡不住已经在链路上的攻击者。别有侥幸心理。
小结
用 2 美元 + 一个 USB 口,给全家网络加一层 DNS 级广告 / 追踪拦截,还能免 USB 远程更新——这个项目的巧思(哈希存 flash)确实值得一看。比树莓派跑 Pi-hole 省电省钱,比纯路由器插件更可控。适合喜欢折腾硬件、又想把家里网络弄干净的人。
注意:出厂开放 AP 和 HTTP 明文是设计取舍,正式用一定改掉默认密码、放在你信得过的网络里。
把一个 $2 的 ESP32-C3 变成 Pi-hole 级的 DNS 广告拦截器(DNS sinkhole),无需外置 PSRAM——53.7 万域名以 40 位 FNV-1a 哈希存进 flash 二分查找,还带 Web 管理面板。





