树莓派Zero打造家庭网络净化与信息显示桌面助手
1. 项目概述与核心价值
如果你和我一样,对无处不在的网络追踪和那些“贴心”到令人不适的个性化广告感到厌烦,同时又希望家里能有个简洁、直观的设备,随时告诉你外面的天气和网络状况,那么这个项目可能就是为你准备的。我利用手头闲置的树莓派Zero,结合开源的Pi-hole广告拦截软件和OpenWeatherMap的天气服务,打造了一个集网络净化与信息显示于一体的桌面小助手。它不仅能从根源上净化你的家庭网络,还能通过几个物理按钮和一块低功耗的E-Paper屏幕,为你提供一目了然的信息和控制体验。整个系统功耗极低,可以7x24小时安静地待在角落工作,非常适合对网络隐私有要求、又喜欢动手折腾的极客或智能家居爱好者。
这个项目的核心,是将两个看似独立的功能——网络层面的广告拦截和物理层面的信息交互——巧妙地整合在一个自制的、带有复古风格的木盒里。你不需要深厚的编程或电子学背景,只要跟着步骤来,就能复现一个既实用又有格调的小工具。接下来,我会从设计思路、硬件搭建、软件配置到代码调试,毫无保留地分享我的整个构建过程,特别是那些官方文档里不会写的“坑”和解决技巧。
2. 整体设计与核心思路拆解
2.1 为什么选择树莓派Zero与Pi-hole的组合?
选择树莓派Zero WH作为硬件核心,主要基于三点考量:功耗、成本与功能平衡。Zero的功耗通常在1-2瓦之间,非常适合作为需要长期运行的网络基础设施。其GPIO引脚完整,足以驱动E-Paper屏幕和多个按钮。而Pi-hole,作为一个开源、免费的DNS级广告拦截器,其原理非常优雅:它将自己设置为家庭网络的DNS服务器,所有设备的域名解析请求都会先经过它。Pi-hole维护着一个庞大的广告域名黑名单,当检测到请求的域名在黑名单中时,便返回一个无效地址(如0.0.0.0)或本地地址,从而阻止设备与广告服务器建立连接。这种方式的优势在于,它是网络层级的拦截,对所有接入网络的设备(手机、电脑、智能电视)都生效,且不依赖于特定浏览器或操作系统。
然而,正如一些实践者指出的,Pi-hole并非万能。例如,对于YouTube等使用复杂域名策略或与内容共用同一域名的广告,拦截效果会大打折扣。但这并不否定其价值。它能有效拦截网页上大量的横幅广告、跟踪脚本、恶意软件域名,显著提升浏览体验和安全性。将Pi-hole部署在树莓派上,相当于为你的家庭网络安装了一个自有的、可完全控制的“净化网关”。
2.2 功能集成与交互设计思路
仅有后台运行的Pi-hole,其存在感是隐形的。为了让这个“网络卫士”变得可感知、可交互,我为其增加了前端显示和物理控制层。
- 信息可视化(E-Paper屏幕):选择1.54英寸的黑白E-Paper(电子墨水屏),是因为其显示效果类似纸张,只在刷新时耗电,显示静态内容时零功耗,非常适合长期显示不变的网络状态或天气信息。
- 物理交互(三个按钮):
- 按钮一(状态查询):一键触发,屏幕显示Pi-hole的实时统计数据,如拦截的广告域名数量、查询总数、网络内活跃客户端等。这让你对网络净化效果有直观感受。
- 按钮二(天气查询):一键触发,通过调用OpenWeatherMap API,轮询显示预设城市的天气概况(温度、湿度、简要天气状况)。信息获取后缓存在本地,避免频繁请求API。
- 按钮三(电源管理):这是一个硬件与软件结合的功能。通过GPIO检测按钮动作,配合脚本实现软关机。再次按下时,由于树莓派的设计,接通电源即可启动。这省去了每次开关机都需要通过SSH登录的麻烦,让设备更像一个独立的家电。
- 自动刷新机制:系统会定时(例如每30分钟)自动刷新屏幕,显示当前网络连接状态(如在线/离线)或时间,确保设备“活着”且信息不过时。
这种设计将后台服务、网络API、物理硬件和用户操作串联成一个闭环,打造了一个功能明确、交互简单的专用设备,而非一个需要复杂操作的小电脑。
3. 硬件准备与机箱制作详解
3.1 物料清单与选型考量
以下是完成本项目所需的全部硬件清单,我会解释关键部件的选型原因:
- 核心控制器:树莓派Zero WH 1个。选择WH(Wireless & Header)版本至关重要,它自带焊好的GPIO排针和Wi-Fi/蓝牙,省去了自己焊接的麻烦,对新手友好。
- 电源:Micro-USB电源线1根。建议使用输出为5V/2.5A以上的优质电源适配器,保证树莓派稳定运行。
- 显示模块:1.54英寸黑白E-Paper显示屏(分辨率200x200)1块。务必确认其驱动芯片为
SSD1681或兼容型号,并购买时索要或找到对应的Python库(如waveshare_epd)。 - 交互部件:常开型轻触按键开关 3个。我选用的是直径10mm的按钮,手感清晰。
- 连接线:母对母杜邦线 6条。用于连接按钮和GPIO。
- 结构材料:
- M2.5黄铜螺柱套装:用于固定树莓派和屏幕。
- 2mm厚透明亚克力板:用于保护脆弱的E-Paper屏幕表面。
- 5mm厚黑色亚克力板:用于制作盒盖。
- 5mm厚胶合板:用于制作内部支撑结构。你也可以使用其他易加工的木材或塑料板。
- 机箱本体:我改造了一个旧抽屉。你可以使用任何尺寸合适的现成木盒、塑料盒,或者完全用板材自制。我最终成品的外尺寸是120x120x90 mm。
注意:购买E-Paper屏幕时,一定要和卖家确认是否提供树莓派的示例代码和库。很多廉价屏幕只提供Arduino库,在树莓派上使用会非常麻烦。
3.2 机箱(PiBox)制作全流程
机箱制作是让项目从“开发板堆叠”升级为“成品设备”的关键一步,需要耐心和一点手工。
3.2.1 外壳改造与开孔我使用的旧抽屉侧面和底板较厚,所以先小心拆解,将侧板和底板裁短至目标尺寸后重新用木胶粘合。如果你从零制作,可以直接切割板材组装。
- 前面板开孔:这是精度要求最高的部分。
- 按钮孔:在面板水平中心线上,钻三个直径10mm的孔,中心间距25mm。使用台钻或手电钻配合开孔器能获得更规整的圆孔。
- 屏幕窗口:在按钮孔上方,开一个34mm x 49mm的矩形窗口。先用小钻头在四角钻孔,再用线锯或雕刻刀小心切割,最后用锉刀修平边缘。
- 线槽:在屏幕窗口下方,开一个15x25mm的矩形槽,用于穿过屏幕的排线。
- 解决按钮安装问题:我的前面板厚8mm,而按钮的螺纹长度可能只有6mm。我的解决方案是:剪裁四段内径7mm、外径10mm、长4mm的橡胶管,用胶水分别粘在三个按钮孔内侧。安装时,按钮螺杆穿过橡胶管,拧上螺母。拧紧时橡胶管会向外膨胀,从而牢牢卡在孔内,既解决了厚度问题,也起到了减震固定作用。
- 底板开孔:在底板左下角开一个细槽,用于电源线引出。
3.2.2 内部支撑结构制作内部需要两个支撑架:一个用于固定树莓派,一个用于固定E-Paper屏幕。
- 树莓派Zero支架:用5mm胶合板切割一个60x35mm的矩形板。树莓派Zero的安装孔位构成一个58mm x 23mm的矩形(注意,不是53mm,原文可能有误)。在胶合板对应位置钻4个M2.5的孔,拧上黄铜螺柱。然后在胶合板中心线两端钻两个M3孔,用于将整个支架固定到机箱底板上。
- E-Paper屏幕支架:切割两块60x20mm的胶合板条。将屏幕放入前面板的窗口,从内部确定其最佳位置。然后用这两块板条像“夹子”一样,从屏幕电路板的左右两侧将其夹住,板条两端用螺丝固定到机箱侧板。在屏幕电路板的安装孔对应位置做好标记,取下屏幕后,在标记处钻孔并安装螺柱,最后再将屏幕拧到螺柱上。这种悬臂梁式的固定方式非常牢固。
- 屏幕保护:按34x49mm尺寸切割2mm厚的透明亚克力板,四角钻孔,用M2螺丝直接固定在屏幕前方的机箱内壁上,与屏幕保持微小间隙即可。
3.2.3 最终打磨与组装用砂纸将所有木制部件边缘打磨光滑,然后上漆或木蜡油。待干燥后,按顺序组装:先安装内部支架,然后布线,接着固定树莓派和屏幕,最后安装前面板的按钮和亚克力保护板。盒盖用黑色亚克力板制作,四角开沉头孔,用螺丝从内部固定,外观整洁。
4. 电路连接与GPIO配置
正确的接线是硬件功能的基础,务必仔细。
4.1 按钮接线与GPIO映射
三个按钮均一端接地(GND),另一端接GPIO引脚,并启用内部上拉电阻。这样,按钮未按下时,GPIO读取为高电平(1);按下时,引脚接地变为低电平(0)。 我将杜邦线剪断,剥出一小段线芯,直接焊接在按钮的引脚上,并套上热缩管绝缘。这样可以得到长度定制、更整洁的连接线。
接线对应关系如下(使用物理引脚编号,即板子上的实际位置编号):
- 按钮1(Pi-hole状态):GPIO ->物理引脚5(GPIO3), GND ->物理引脚6。
- 按钮2(天气查询):GPIO ->物理引脚33(GPIO13), GND ->物理引脚34。
- 按钮3(电源开关):GPIO ->物理引脚40(GPIO21), GND ->物理引脚39。
重要提示:树莓派GPIO有物理引脚编号(Board)和BCM编号两种模式。在Python的RPi.GPIO库中,需要明确指定使用哪一种。我的代码中使用的是物理引脚编号,与上面的描述一致。如果你参考的代码使用BCM编号(如GPIO3对应BCM 2),务必统一,否则按钮会无响应。
4.2 E-Paper屏幕接线详解
1.54英寸E-Paper通常使用SPI接口通信,需要连接8根线。请以你的屏幕说明书为准,以下是最常见的接法(同样使用物理引脚编号):
- VCC->引脚17(3.3V电源)。注意:有些屏幕需要5V,请确认你的屏幕电压要求。
- GND->引脚20(GND)。
- DIN (MOSI)->引脚19(GPIO10)。
- CLK (SCLK)->引脚23(GPIO11)。
- CS (CE0)->引脚24(GPIO8)。这是SPI0的片选0。
- DC->引脚22(GPIO25)。用于区分传输的是数据还是命令。
- RST->引脚11(GPIO17)。复位引脚。
- BUSY->引脚18(GPIO24)。屏幕忙状态检测。
接好线后,务必检查再检查,特别是电源和地线不要接反。然后将树莓派和屏幕分别拧到对应的支架螺柱上。
5. 软件系统配置与核心脚本解析
5.1 基础系统与Pi-hole安装
树莓派Zero性能有限,因此选择Raspberry Pi OS Lite(32位)这个无桌面环境的最小化系统。使用Raspberry Pi Imager工具刷录系统镜像时,记得在设置中提前启用SSH并配置Wi-Fi,这样刷好后就能直接通过网络登录。
首次通过SSH登录后,第一件事是更新系统:sudo apt update && sudo apt upgrade -y。
接下来安装Pi-hole。官方提供的一键安装脚本非常方便:
curl -sSL https://install.pi-hole.net | bash安装过程是交互式的,会询问一些配置:
- 上游DNS服务器:建议选择可靠的公共DNS,如Cloudflare (1.1.1.1) 或 Google (8.8.8.8)。
- 协议:选择同时启用IPv4和IPv6。
- 管理后台密码:务必设置一个强密码并牢记。安装完成后会显示,你也可以用
pihole -a -p命令修改。 - Web管理界面:选择安装,这样可以通过浏览器访问Pi-hole的管理面板。
安装完成后,记下分配给树莓派的IP地址(可以用hostname -I查看)。你需要在家庭路由器的设置中,将DHCP服务器分配的DNS地址改为这个IP地址,或者直接将设备的DNS手动设置为这个IP,这样所有流量才会经过Pi-hole。
5.2 防火墙与SPI接口配置
由于设备长期在线,启用简单防火墙ufw是个好习惯。
sudo apt install ufw # 开放Pi-hole所需端口 sudo ufw allow 80/tcp # Web管理界面 sudo ufw allow 53/tcp # DNS over TCP sudo ufw allow 53/udp # DNS over UDP # 允许SSH,否则远程连接会断开! sudo ufw allow ssh sudo ufw enable启用SPI接口以驱动E-Paper屏幕:
sudo raspi-config在界面中依次选择Interface Options->SPI->Yes,完成后重启。
5.3 Python环境与依赖库安装
安装必要的Python库:
sudo apt-get install python3-pip python3-pil python3-numpy -y sudo pip3 install RPi.GPIO spidev requests scheduleRPi.GPIO:控制GPIO引脚。spidev:SPI通信驱动。requests:用于调用Pi-hole和OpenWeatherMap的API。schedule:实现定时任务(如自动刷新)。PIL (Pillow)和numpy:图像处理,用于生成显示内容。
5.4 核心项目脚本解析与定制
我将主要功能封装在几个Python类中,结构清晰。你需要从项目文件(如PiBox_project.tar.gz)中获取代码。
5.4.1 主程序逻辑 (main.py)这是程序的入口,一个无限循环,主要做三件事:
- 检测按钮事件:使用
RPi.GPIO的add_event_detect功能,为每个按钮绑定下降沿(按下)事件回调函数。 - 执行对应功能:当检测到按钮按下,调用
Pibox类中相应的方法(如get_pihole_stats,get_weather)获取数据并生成图像。 - 刷新屏幕:将生成的图像发送给
EPD(E-Paper驱动)类进行显示。 - 定时任务:使用
schedule库,每隔一段时间(如30分钟)自动执行一次网络状态检查并刷新显示。
关键配置修改(打开main.py文件):
# 第30行附近:设置你的Pi-hole管理IP pihole_address = "http://192.168.1.100" # 改为你树莓派的实际IP # 第31行:设置你的OpenWeatherMap API Key API_key = "your_actual_api_key_here" # 去OpenWeatherMap官网免费注册获取 # 第32行:设置你关心的城市 cities_info = { '北京': [39.9042, 116.4074], '上海': [31.2304, 121.4737], '深圳': [22.5431, 114.0579] # 可以继续添加 }5.4.2 Pi-hole状态获取 (pibox.py中的get_pihole_stats方法)Pi-hole提供了一个简洁的API用于获取统计信息。我们调用其管理API(无需密码即可获取基础统计):
import requests def get_pihole_stats(self): try: # 调用汇总API api_url = f"{self.pihole_address}/admin/api.php?summary" response = requests.get(api_url) data = response.json() domains_blocked = data['domains_being_blocked'] ads_blocked_today = data['ads_blocked_today'] dns_queries_today = data['dns_queries_today'] # ... 其他数据 # 使用PIL创建一张200x200的黑色图像,然后在上面画白色文字 # 将格式化后的字符串绘制到图像上 return image # 返回这个图像对象 except Exception as e: # 出错时返回错误信息图像 return self.display_error("Pi-hole API Error")实操心得:Pi-hole的API访问速度很快,但为了界面流畅,最好在子线程中执行网络请求,避免阻塞主循环导致按钮响应迟钝。我的示例代码中使用了简单的同步请求,对于家庭网络环境足够用。
5.4.3 天气信息获取 (pibox.py中的get_weather方法)OpenWeatherMap的免费API每小时可请求60次,足够个人使用。我们调用其current weather接口。
def get_weather(self): # 轮询显示预设城市 current_city = self.cities[self.city_index] self.city_index = (self.city_index + 1) % len(self.cities) # 下次按按钮换城市 lat, lon = self.cities_info[current_city] url = f"https://api.openweathermap.org/data/2.5/weather?lat={lat}&lon={lon}&appid={self.api_key}&units=metric" # units=metric 表示使用摄氏度 try: response = requests.get(url) data = response.json() temp = data['main']['temp'] humidity = data['main']['humidity'] weather_desc = data['weather'][0]['description'] # 将信息绘制到图像上,例如:“北京: 22°C, 45%湿度,多云” return image except Exception as e: return self.display_error("Weather API Error")注意事项:API Key是私密的,不要上传到公开的代码仓库。免费API有调用频率限制,不要在循环里疯狂调用。我的设计是每次按下按钮才获取一次,并轮流显示不同城市,完全在限制范围内。
5.4.4 电源按钮功能实现这是硬件与软件结合的一个小技巧。按钮连接GPIO21和GND。在代码中,我们检测该按钮的按下事件,然后触发关机命令。
def shutdown_callback(channel): # 在屏幕上显示“Shutting down...”信息 display_shutdown_message() time.sleep(1) # 执行关机命令 os.system("sudo shutdown -h now") # 在初始化中设置事件检测 GPIO.add_event_detect(power_button_pin, GPIO.FALLING, callback=shutdown_callback, bouncetime=300)关机后,树莓派会彻底断电。再次通电时,由于我们配置了服务自启动(后面会讲),系统会自动启动我们的主程序。注意:shutdown -h now需要sudo权限,这就是为什么我们必须用sudo python3 main.py来运行脚本。
5.5 配置系统服务实现开机自启
我们不希望每次断电重启后都要手动SSH进去启动程序。创建系统服务是最优雅的方式。
- 在项目文件夹的
service子目录下,有一个pibox.service文件。用编辑器打开它。 - 修改
WorkingDirectory和ExecStart路径,指向你的main.py文件所在目录。[Unit] Description=PiBox Service After=multi-user.target [Service] Type=simple WorkingDirectory=/home/pi/PiBox_project # 你的项目绝对路径 ExecStart=/usr/bin/python3 /home/pi/PiBox_project/main.py Restart=on-abort User=pi [Install] WantedBy=multi-user.target - 将此服务文件复制到系统目录并启用:
sudo cp pibox.service /etc/systemd/system/ sudo systemctl daemon-reload sudo systemctl enable pibox.service sudo systemctl start pibox.service - 检查服务状态:
sudo systemctl status pibox.service,看到active (running)即表示成功。
6. 常见问题排查与优化技巧
即使完全按照步骤,你也可能会遇到一些问题。这里是我在搭建过程中遇到的一些典型问题及解决方法。
6.1 硬件与连接问题
问题1:按钮按下无反应。
- 检查接线:确认按钮的GPIO引脚和GND引脚是否接对,接触是否良好。用万用表通断档测量按钮按下时两端是否导通。
- 检查GPIO模式:确认代码中
GPIO.setmode(GPIO.BOARD)使用的是物理引脚编号,并与你的接线图一致。 - 检查上拉电阻:代码中是否设置了
GPIO.setup(pin, GPIO.IN, pull_up_down=GPIO.PUD_UP)。如果没有内部上拉,引脚处于悬空状态,读取值会不稳定。
问题2:E-Paper屏幕白屏或全黑,不显示内容。
- 检查SPI是否启用:运行
ls /dev/spi*,应该能看到/dev/spidev0.0和/dev/spidev0.1。如果没有,返回raspi-config确认SPI已开启。 - 检查接线:尤其是VCC电压(3.3V还是5V)、GND和关键的SPI引脚(DIN, CLK, CS)。一根线接错就可能不工作。
- 检查驱动库:确保安装的
waveshare_epd库与你的屏幕型号(如epd1in54_V2)完全匹配。不同尺寸、版本的屏幕驱动可能不兼容。 - 初始化顺序:屏幕驱动通常有严格的初始化序列。确保你的代码中正确调用了
epd.init()、epd.Clear()等函数。
6.2 软件与网络问题
问题3:Pi-hole安装后,设备无法上网。
- 检查DNS设置:确保你的路由器DHCP下发的DNS服务器地址,或你设备手动设置的DNS地址,是树莓派Pi-hole的IP地址。
- 检查Pi-hole服务状态:在树莓派上运行
pihole status,查看所有服务是否正常运行。 - 检查防火墙:如果你启用了
ufw,确认已经按照前文放行了53端口(UDP/TCP)。可以临时禁用防火墙测试:sudo ufw disable。 - 查看查询日志:在Pi-hole管理后台(
http://树莓派IP/admin)的“查询日志”中,看是否有来自你设备的查询请求,以及这些请求是被拦截还是放行了。
问题4:天气信息无法获取,API报错。
- 检查API Key:首先确认在代码中填写的API Key是否正确,且没有多余的空格。
- 检查网络连接:在树莓派上运行
ping api.openweathermap.org,看是否能通。如果树莓派本身无法访问外网,自然无法获取天气。 - 查看API响应:在代码中加入临时打印语句,打印出
response.status_code和response.text。常见的错误如401表示API Key无效,429表示请求过于频繁。 - 城市坐标:确认你填写的城市经纬度坐标格式正确,且在OpenWeatherMap的服务范围内。
问题5:开机自启动服务失败。
- 检查服务状态详情:运行
sudo journalctl -u pibox.service -f可以实时查看该服务的详细日志输出,里面通常会有具体的错误信息。 - 检查路径和权限:确保
WorkingDirectory和ExecStart中的路径绝对正确,并且pi用户有该目录和main.py文件的读取执行权限。 - 检查Python依赖:系统服务运行时环境可能与SSH登录后的环境略有不同。尝试在服务文件中使用绝对路径指定python解释器(如
/usr/bin/python3),并确保所有依赖库(如requests,RPi.GPIO)已全局安装(使用sudo pip3 install)。
6.3 功能优化与扩展建议
- 降低E-Paper屏幕刷新频率与防残影:电子墨水屏全屏刷新时会有闪烁,且频繁刷新易产生“残影”。优化策略是:
- 使用局部刷新:在
waveshare_epd库中寻找displayPartial或类似函数,只更新屏幕上变化的部分(如变化的数字),而不是整个屏幕。这能极大提升刷新速度和观感。 - 定时全刷:即使使用局部刷新,也建议每刷新10-20次后,强制进行一次全屏刷新以清除残影。可以在代码中设置一个计数器。
- 使用局部刷新:在
- 增加更多信息源:
pibox.py的类结构很容易扩展。你可以仿照现有方法,添加新的功能,例如:- 系统状态:显示树莓派的CPU温度、负载、可用存储空间。
- 网络速度:定时测试网络上传下载速度并显示。
- 日历/日程:连接Google Calendar或本地日历文件,显示今日日程。
- 只需要新建一个方法,获取数据、生成图像并返回,然后在主程序的按钮回调或定时任务中调用它即可。
- 改善电源管理:目前的软关机方案需要按两次按钮(一次触发关机,一次重新上电)。可以考虑加入一个自锁电路或使用带有状态指示的按钮,实现单次按压进行“关机/唤醒”的切换,但这需要额外的硬件电路设计。
- 美化显示界面:使用PIL库可以绘制更复杂的图形。可以提前用电脑设计好UI模板(背景、图标、字体),保存为小尺寸的BMP或PNG图片,然后在树莓派上加载和拼接文字信息,这样显示效果会更美观。
这个项目最吸引人的地方在于,它从一个具体的需求(去广告、看天气)出发,通过硬件制作和软件编程,最终形成了一个独一无二的、贴合个人使用习惯的实体产品。整个过程充满了探索和解决问题的乐趣。当你按下按钮,屏幕亮起,看到今天拦截了上千个广告域名,或是看到远方城市的阳光明媚时,那种成就感远非购买一个成品所能比拟。希望这份详细的指南能帮助你顺利搭建属于自己的家庭网络助手。
