1. 从一块吃灰的树莓派说起:我为什么折腾这套智能家居系统
手头这块树莓派4B买了快两年,最开始刷了个游戏系统接电视当怀旧主机,玩了不到一周就扔抽屉里了。后来搬家,发现家里各种遥控器、开关、传感器越堆越多,空调一个遥控、灯一个遥控、窗帘还得手动拉,我就琢磨着能不能用这块板子把家里这些零碎玩意儿串起来。说白了,树莓派智能家居系统这件事,核心就是用一块低功耗的Linux小板子当大脑,把传感器、继电器、摄像头、红外发射管这些外设管起来,再通过手机或者网页统一控制。
这套东西能干什么?往小了说,你可以用手机开关客厅的灯、定时给鱼缸补光、出门在外看看家里猫在干嘛;往大了说,它能接入温湿度、人体感应、烟雾报警,做成一套完整的家庭自动化中枢。适合谁来参考?我觉得有三类人:一是手里正好有树莓派在吃灰的,二是电子、物联网相关专业要做课程设计或者毕业设计的同学,三是单纯喜欢折腾、想搞明白智能家居底层逻辑的爱好者。你不需要是Linux高手,但至少得会烧录系统、会用SSH连上去敲几行命令。
我前后花了大概三周时间,从选传感器、画接线图、写Python脚本,到做Web控制界面、配开机自启,中间踩了不少坑,也推翻过两版方案。下面我把整个思路、选型逻辑、实操步骤和踩坑记录完整梳理一遍,你照着做基本能复现一套能用的系统。
2. 整体方案怎么定:先想清楚你要控制什么,再决定用什么
2.1 核心架构的三种路线选择
动手之前我先在纸上列了个清单,把家里需要控制的东西分成三类:开关型(灯、风扇、插座)、信号型(红外遥控的空调、电视)、感知型(温湿度、人体、门磁)。分完之后发现,不同设备接入方式完全不一样,于是就有了三种架构路线。
第一种是树莓派直连GPIO。传感器和继电器直接插在树莓派的40针排针上,Python脚本读写引脚电平。优点是延迟极低、结构简单、不依赖网络;缺点是树莓派引脚只有那么多,而且3.3V逻辑电平驱动能力弱,接大功率负载必须加继电器或者驱动板。我一开始图省事直接拿GPIO接了个5V继电器模块,结果树莓派时不时重启,后来查了半天才发现是继电器线圈反向电动势干扰了电源,加了个续流二极管和独立5V供电才稳。
第二种是树莓派当网关,下挂单片机节点。比如用ESP8266或者STM32做子节点,通过串口或者无线模块跟树莓派通信,树莓派只负责逻辑调度和界面。这条路适合设备多、布线长的场景,我有个朋友家里三层楼,就是每层放一个ESP32节点,树莓派在二楼统一管。缺点是通信协议要自己定,调试起来麻烦。
第三种是纯软件层集成。树莓派不直接碰硬件,而是跑Home Assistant、Node-RED这类开源平台,通过插件去对接米家、涂鸦这些现成设备。这条路最省事,但依赖第三方云服务,断网就抓瞎,而且很多插件更新后接口就变了。
我最后选的是第一种为主、第三种为辅的混合方案:核心的灯、传感器走GPIO直连,保证断网也能本地控制;空调这种红外设备用红外发射管加编码库搞定;手机端用Flask搭个轻量Web页面,局域网内直接访问。这样既不用依赖外网,又能保证响应速度。
2.2 硬件选型:别贪便宜,也别盲目追新
树莓派型号上,我手头是4B 4GB版本,跑这套系统绰绰有余。如果你还没买,我的建议是:4B起步,预算够就上5。3B+也能跑,但内存小,同时开Web服务和几个Python进程会有点吃力。树莓派5的PCIe接口对这套系统其实用不上,除非你要接NVMe硬盘做视频存储,否则多花的钱不如买个好点的电源和散热。
传感器和模块这块,我列了个实际用到的清单和踩坑评价:
| 模块 | 型号/规格 | 接口方式 | 实际体验 |
|---|---|---|---|
| 温湿度 | DHT22 | 单总线GPIO | 精度够用,但读取间隔不能低于2秒,否则数据乱跳 |
| 人体感应 | HC-SR501 | GPIO数字输入 | 灵敏度过高,后来调了电位器才不误触发 |
| 继电器 | 4路5V光耦隔离 | GPIO输出 | 必须选光耦隔离版,普通版会干扰树莓派 |
| 红外收发 | IR发射管+接收头 | GPIO | 编码库用LIRC,学习遥控码比较折腾 |
| 摄像头 | OV5647 | CSI排线 | 官方系统直接支持,但夜视要另配红外补光 |
| 舵机 | SG90 | GPIO PWM | 控制窗帘用,抖动问题靠独立供电解决 |
电源是整套系统最容易被忽视的环节。树莓派4B官方推荐5V 3A,我一开始用了个旧手机充电器,标称5V 2A,结果接上继电器和摄像头后频繁掉电。后来换了个5V 4A的工业电源,同时给树莓派和继电器供电,问题才消失。记住一个原则:树莓派和外设尽量分开供电,共地但不共电源,能避免一大半莫名其妙的故障。
2.3 软件栈:轻量优先,别一上来就上重型框架
系统我刷的是Raspberry Pi OS Lite 64位,无桌面版本。为什么不装桌面?因为这套系统跑起来之后根本不需要显示器,SSH连上去操作就行,桌面环境白白吃掉几百MB内存。烧录用官方Imager,提前在设置里把SSH打开、WiFi配好,插卡开机就能连。
编程语言用Python 3,库主要用这几个:RPi.GPIO或者gpiozero控制引脚,Adafruit_DHT读温湿度,Flask搭Web服务,APScheduler做定时任务,sqlite3存历史数据。有人推荐用Django,我觉得没必要,这套系统页面就几个,Flask几十行代码就搞定了,启动还快。
数据库用SQLite,单文件、零配置,存个温湿度历史、开关记录完全够用。如果你要存摄像头视频,那就得考虑外接硬盘或者用树莓派5的NVMe了。
3. 核心细节逐个拆:从接线到代码的关键点
3.1 GPIO接线:一张图省三天调试时间
树莓派4B的40针排针,我实际用到的引脚如下,你接线前一定对照官方引脚图确认,不同型号引脚定义有差异:
- 温湿度DHT22:数据脚接GPIO4(物理引脚7),VCC接3.3V(引脚1),GND接引脚6
- 人体感应HC-SR501:输出接GPIO17(引脚11),VCC接5V(引脚2),GND接引脚9
- 四路继电器:IN1-IN4分别接GPIO18、23、24、25(引脚12、16、18、22),VCC接5V,GND共地
- 红外发射管:信号脚接GPIO22(引脚15),需要串联一个100欧姆限流电阻
- 舵机:PWM信号接GPIO12(引脚32),电源单独走5V 2A
接线时有个细节:DHT22的数据脚需要接一个4.7k到10k的上拉电阻到3.3V,否则读出来的全是None。我一开始没接,以为是库的问题,换了三个库都不行,后来翻数据手册才发现这个要求。
继电器模块的选择上,务必选光耦隔离+低电平触发的版本。低电平触发意味着GPIO输出0时继电器吸合,这样树莓派启动瞬间引脚默认高电平,不会误触发。我第一版用的是高电平触发,结果每次重启树莓派,灯都会闪一下,后来换了模块才解决。
3.2 温湿度采集:为什么你的DHT22总是读失败
DHT22这个传感器便宜好用,但脾气不小。我总结了几条实操经验:
第一,读取间隔必须大于2秒。DHT22内部采样率有限,你如果每秒读一次,它会返回上一次的缓存值甚至乱码。我在代码里用APScheduler设了3秒一次,稳得很。
第二,电源要干净。DHT22对电源纹波敏感,如果和继电器共用一个5V电源,继电器吸合瞬间的压降会导致读数失败。我后来给传感器单独走了一路3.3V,失败率从30%降到几乎为零。
第三,线长别超过1米。单总线协议对线材电容敏感,线太长信号边沿变缓,读出来的就是校验错误。我最初把传感器放在阳台,线拉了3米,怎么调都不行,后来挪到室内0.5米线,一次成功。
代码层面,我用的是Adafruit_DHT库,读取逻辑加了个重试机制:
import Adafruit_DHT import time def read_dht22(pin=4, retries=3): for i in range(retries): humidity, temperature = Adafruit_DHT.read_retry(Adafruit_DHT.DHT22, pin) if humidity is not None and temperature is not None: return round(temperature, 1), round(humidity, 1) time.sleep(2) return None, None这个重试机制很关键,单次读取失败很正常,重试三次基本都能拿到数据。
3.3 继电器控制:别让树莓派被反向电动势干掉
继电器控制逻辑本身很简单,GPIO输出高低电平就行。但硬件上有两个坑必须避开:
坑一:反向电动势。继电器线圈断电瞬间会产生几十伏的反向电压,直接冲击树莓派GPIO,轻则引脚损坏,重则整块板子报废。解决办法是选带光耦隔离和续流二极管的继电器模块,或者自己在线圈两端并联一个1N4007二极管。
坑二:共地问题。如果继电器用独立5V电源,必须把电源的GND和树莓派的GND连在一起,否则控制信号没有参考电平,继电器会乱动。我一开始忘了共地,继电器时好时坏,查了两个小时才想起来。
代码上我用gpiozero库,比RPi.GPIO更简洁:
from gpiozero import OutputDevice from time import sleep relay1 = OutputDevice(18, active_high=False, initial_value=False) def toggle_light(state): if state: relay1.on() else: relay1.off()active_high=False表示低电平触发,initial_value=False保证启动时继电器是断开状态,避免上电瞬间灯亮一下。
3.4 红外遥控:让树莓派学会你家的空调码
红外这块是最折腾的。树莓派本身没有红外接收头,你得先接一个IR接收模块(比如VS1838B),用LIRC或者irrecord命令学习遥控器的编码,把码存下来,再用发射管发出去。
学习过程大概是:安装LIRC,配置/boot/config.txt加载红外驱动,重启后用irrecord跟着提示按遥控器按键,生成一个配置文件。这个过程因遥控器品牌而异,有的空调码特别长,要按几十次。我学格力空调的码花了大概二十分钟。
发射的时候,用irsend命令或者Python的pylirc库调用。注意红外发射管的方向性很强,你得把它对准空调接收窗,中间不能有遮挡。我最初把发射管焊在板子上随便一放,十次有三次没反应,后来用热熔胶固定了个角度,才稳定下来。
3.5 Web控制界面:Flask三十行代码搞定手机控制
界面我没用任何前端框架,就一个HTML页面加几个按钮,Flask渲染。核心路由长这样:
from flask import Flask, render_template, request, jsonify app = Flask(__name__) @app.route('/') def index(): return render_template('index.html') @app.route('/api/control', methods=['POST']) def control(): device = request.json.get('device') state = request.json.get('state') # 调用对应的控制函数 return jsonify({'status': 'ok'})前端用fetch发POST请求,按钮点击后更新状态。页面加了个viewportmeta标签,手机上显示不会太小。整个页面加载不到50KB,局域网内秒开。
有个细节:Flask默认只监听127.0.0.1,你要让手机访问,得改成app.run(host='0.0.0.0', port=5000)。但这样局域网内任何人都能访问,所以我又加了个简单的HTTP Basic认证,至少挡一下。
4. 完整实操流程:从烧录系统到开机自启
4.1 系统烧录与基础配置
第一步,下载Raspberry Pi Imager,选Raspberry Pi OS Lite 64位。点右下角齿轮图标,提前配置:开启SSH、设置WiFi账号密码、设置用户名密码、时区选Asia/Shanghai。这样烧录完插卡开机,等一分钟就能SSH连上,不用接显示器键盘。
第二步,SSH连上去之后先换源。国内访问官方源慢,换成清华或者中科大的镜像源,apt update和apt upgrade能快好几倍。换源命令网上很多,注意树莓派5和4B的源地址略有不同,别抄错了。
第三步,安装依赖:
sudo apt install python3-pip python3-flask python3-rpi.gpio python3-gpiozero pip3 install Adafruit_DHT APScheduler如果Adafruit_DHT装不上,可能是缺少编译工具,先sudo apt install python3-dev再装。
4.2 代码组织与定时任务
我把代码分成几个文件:sensors.py负责读传感器,actuators.py负责控制继电器和红外,scheduler.py负责定时任务,app.py是Flask入口。这样改起来清晰,不会一个文件几百行。
定时任务用APScheduler,比如每3秒读一次温湿度存数据库,每天早上7点开客厅灯,晚上11点关。配置如下:
from apscheduler.schedulers.background import BackgroundScheduler scheduler = BackgroundScheduler() scheduler.add_job(read_and_store, 'interval', seconds=3) scheduler.add_job(turn_on_light, 'cron', hour=7, minute=0) scheduler.start()注意APScheduler在Flask里要用BackgroundScheduler,否则会阻塞Web服务。
4.3 开机自启:systemd比rc.local靠谱
让这套系统开机自动跑起来,我试过三种方法:rc.local、crontab @reboot、systemd。最后选了systemd,因为日志清晰、重启策略灵活。
创建一个服务文件/etc/systemd/system/smarthome.service:
[Unit] Description=Smart Home Service After=network.target [Service] User=pi WorkingDirectory=/home/pi/smarthome ExecStart=/usr/bin/python3 /home/pi/smarthome/app.py Restart=always RestartSec=5 [Install] WantedBy=multi-user.target然后sudo systemctl enable smarthome,重启后自动运行。Restart=always保证程序崩溃后5秒自动拉起,这点很实用,我有次代码里有个未捕获异常,程序半夜挂了,第二天发现它自己重启了十几次,至少没彻底停摆。
4.4 数据存储与查询
SQLite数据库就一个文件,建两张表:一张存温湿度历史,一张存设备开关记录。查询用Flask再开个接口,返回JSON,前端用Chart.js画个曲线。数据量大了之后(比如跑了一个月),查询会变慢,加个索引或者定期清理旧数据就行。我设的是保留最近30天,每天凌晨删一次。
5. 常见问题与排查技巧实录
5.1 树莓派频繁重启或死机
这是最吓人的问题,我遇到过两次。第一次是电源功率不够,换了5V 4A电源解决。第二次是继电器反向电动势干扰,加了光耦隔离模块解决。排查思路:先看/var/log/syslog有没有 undervoltage 警告,有就是电源问题;没有的话,拔掉所有外设只留电源,看是否还重启,逐步加回外设定位。
5.2 传感器读数不稳定
DHT22读数失败最常见,按前面说的检查上拉电阻、电源、线长。人体感应HC-SR501误触发,调模块上的两个电位器:一个调灵敏度,一个调延时。我调到灵敏度最低、延时最短,才不误报。
5.3 手机访问不了Web页面
先确认树莓派和手机在同一个局域网,然后ping一下树莓派IP。如果ping通但网页打不开,检查Flask是不是监听0.0.0.0,防火墙是不是挡了5000端口。树莓派默认没有防火墙,但如果你装了ufw,记得放行。
5.4 红外发射不灵
发射管对准接收窗,距离别超过3米。如果还不行,用手机摄像头对着发射管,按发射键时看有没有紫光闪烁,有闪烁说明发射管工作,问题在编码;没闪烁说明电路问题,检查限流电阻和接线。
5.5 系统跑一段时间后变慢
大概率是日志文件或者数据库太大。journalctl --vacuum-size=50M清理系统日志,数据库定期删旧数据。另外Python进程如果内存泄漏,用systemd的MemoryMax限制一下,超了自动重启。
| 问题现象 | 可能原因 | 排查方法 | 解决措施 |
|---|---|---|---|
| 树莓派频繁重启 | 电源功率不足 | 查syslog undervoltage | 换5V 4A电源 |
| 继电器误动作 | 反向电动势干扰 | 拔掉继电器测试 | 加光耦隔离或续流二极管 |
| DHT22读数失败 | 缺上拉电阻/线太长 | 换短线测试 | 加4.7k上拉,线长<1米 |
| 手机打不开页面 | Flask监听地址错误 | 树莓派本地curl测试 | 改host为0.0.0.0 |
| 红外无反应 | 发射管方向偏 | 手机摄像头观察 | 调整角度固定 |
6. 后续还能怎么扩展
这套系统跑稳之后,我又加了几个功能。一个是接OV5647摄像头做定时拍照,用raspistill命令每半小时拍一张,存到外接U盘,出门在外可以看家里情况。另一个是接了个小喇叭,用espeak做语音播报,比如温度超过30度就播报提醒。还有人用树莓派5跑YOLOv5做本地人脸识别,那个对算力要求高,4B跑起来很吃力,5代配合NPU加速才勉强能用。
如果你要做毕业设计或者课程项目,我建议在现有基础上加个数据可视化大屏,用ECharts画温湿度曲线和设备状态,答辩的时候展示效果很好。或者加个语音控制,用离线语音识别模块,说“开灯”就开灯,比手机点按钮更有科技感。
最后分享一个我踩过的坑:别在树莓派上直接跑重型数据库或者Docker一堆容器,4B那点内存和CPU扛不住。保持轻量,一个Python进程加SQLite,稳定运行几个月没问题。我现在的系统已经连续跑了四个月,除了中间停电一次,没出过毛病。