1. 从"手动开关"到"按需散热":这个温控风扇到底解决了什么
夏天一到,桌面上那台小风扇基本就没停过。但真正用过的人都知道,一直吹着不仅费电,噪音也烦人,尤其是晚上安静的时候,风扇嗡嗡转个不停,体验其实并不好。更关键的是,很多时候设备根本没那么热,风扇却一直在全速运转,这本身就是一种浪费。
这次要聊的,就是用树莓派做一个自动温控小风扇。它的核心逻辑非常朴素:温度高了就转,温度低了就停,温度越高转得越快。听起来简单,但真正动手做的时候,你会发现里面涉及的东西比想象中多——温度传感器怎么选、风扇怎么驱动、树莓派的GPIO能不能直接带得动、PWM调速怎么实现、阈值怎么定才合理,这些都是绕不开的问题。
这个项目适合谁?如果你刚接触树莓派,想找一个既有实际用途、又能把GPIO输入输出、传感器读取、PWM控制这几个基础知识点串起来的练手项目,那这个再合适不过。如果你已经玩过一阵树莓派,想给机箱、路由器、或者某个发热设备加一套智能散热,这个方案也能直接拿来改。
我自己的出发点更实际一点:手头有个小设备长期运行,外壳摸上去温热,但又不至于烫手。装个风扇一直吹吧,灰尘积得快、噪音也大;不装吧,心里又不踏实。最后干脆用树莓派做了套温控,温度到了才转,平时安安静静。实测下来这套逻辑跑了大半年,稳定得很。
下面我会把整个项目的思路、选型、接线、代码、调试、踩坑全部拆开讲清楚。不是那种"复制粘贴就能跑"的流水账,而是把每个决策背后的原因讲明白,这样你遇到类似需求时能自己举一反三。
2. 温控风扇的三个核心部件与选型逻辑
2.1 温度传感器:为什么我最终选了DS18B20而不是DHT11
温度传感器是这个项目的"眼睛",选错了后面全是麻烦。市面上树莓派能用的温度传感器主要就那么几种,我列个表对比一下,你一看就明白。
| 传感器型号 | 测量精度 | 接口方式 | 响应速度 | 价格区间 | 适用场景 |
|---|---|---|---|---|---|
| DHT11 | ±2℃ | 单总线 | 慢(1秒以上) | 极低 | 对精度要求不高的湿度温度一起测 |
| DHT22 | ±0.5℃ | 单总线 | 较慢(2秒) | 低 | 温湿度都要,精度要求中等 |
| DS18B20 | ±0.5℃ | 单总线(1-Wire) | 快(750ms) | 低 | 纯测温,多点组网 |
| BMP280 | ±1℃ | I2C/SPI | 快 | 中 | 需要气压数据 |
| LM75 | ±2℃ | I2C | 快 | 低 | 板载温度监测 |
我一开始用的是DHT11,原因很简单——便宜、教程多。但实际用下来问题很明显:它的温度精度是±2℃,意味着真实温度28℃的时候,它可能报26℃也可能报30℃。对于温控风扇来说,这个误差直接导致阈值判断不准,风扇该转的时候不转,不该转的时候瞎转。而且DHT11的采样间隔至少要1秒,读得太频繁还会返回错误值。
后来换成了DS18B20,体验完全不一样。它是数字信号输出,抗干扰能力强,精度±0.5℃,而且支持多个传感器挂在同一根数据线上(1-Wire总线),以后想同时监测多个位置的温度也很方便。价格也就几块钱,性价比很高。
注意:DS18B20一定要买带防水探头或者带金属外壳的版本,裸封装的TO-92管子测的是它自己周围的空气温度,贴在发热源上反而不准。如果你要测的是设备外壳温度,用金属探头贴上去更靠谱。
2.2 风扇:5V小风扇和12V风扇的驱动差异
风扇这块,很多人第一反应是"树莓派不是有5V引脚吗,直接接上不就行了"。这个想法很危险,我后面会专门讲为什么。先说选型。
常见的散热风扇分两种:5V和12V。5V风扇可以直接用树莓派的5V引脚供电(注意是供电,不是控制),12V风扇则需要额外的12V电源。从转速和风量来看,同样尺寸下12V风扇通常更强,但噪音也更大。
我选的是5V的4010风扇(40mm×40mm×10mm),原因有三:一是体积小,适合给小设备散热;二是5V供电可以直接从树莓派取电,不用额外电源;三是它的工作电流一般在0.1A到0.2A之间,树莓派的5V引脚能承受。
但这里有个关键点:风扇的供电和调速是两回事。供电是给风扇通电让它转,调速是控制它转多快。5V风扇如果直接接5V和GND,它就是全速转,没法调速。要实现调速,必须用PWM(脉冲宽度调制),而PWM信号不能直接驱动风扇,中间需要一个开关元件。
2.3 驱动元件:为什么不能直接用GPIO驱动风扇
这是新手最容易踩的坑,我必须重点讲。
树莓派的GPIO引脚输出的是3.3V的逻辑信号,单个引脚的安全电流一般在16mA左右,所有引脚加起来不超过100mA左右(不同型号略有差异)。而一个5V小风扇的工作电流通常在100mA到200mA,启动瞬间的冲击电流可能更大。
如果你把风扇直接接到GPIO引脚上,会发生什么?轻则风扇转不动或者转得很慢,重则GPIO引脚烧毁,甚至连累整个树莓派。这不是危言耸听,我见过有人这么干,结果树莓派直接开不了机。
所以必须用驱动元件。常见方案有三种:
- NPN三极管(如S8050、2N2222):便宜,适合小电流风扇,但只能单向驱动,调速效果一般。
- N沟道MOS管(如IRLZ44N、AO3400):导通电阻小,适合稍大电流,PWM调速效果好,是我推荐的方案。
- 继电器模块:完全隔离,适合大功率设备,但继电器有机械寿命,且不能PWM调速,只能开或关。
我最终用的是AO3400 MOS管,配合一个1kΩ的栅极电阻和一个10kΩ的下拉电阻。AO3400是逻辑电平MOS管,3.3V就能完全导通,导通电阻只有几十毫欧,驱动小风扇绰绰有余。栅极电阻用来限制瞬间电流,下拉电阻保证GPIO没有输出时MOS管可靠关断,避免风扇乱转。
提示:如果你不想自己搭电路,也可以直接买现成的"MOS管驱动模块",几块钱一个,接线更简单。但自己搭一遍能学到更多东西,建议至少试一次。
3. 接线图与电路搭建:每一步都要想清楚为什么
3.1 DS18B20的接线:那个4.7kΩ上拉电阻不能省
DS18B20有三根线:VCC(红)、GND(黑)、DQ(黄/白,数据线)。接线本身不复杂,但有一个细节必须注意:DQ数据线需要接一个4.7kΩ的上拉电阻到VCC。
为什么?因为1-Wire总线是"开漏"结构,设备只能把线拉低,不能主动拉高。没有上拉电阻的话,总线在空闲时处于悬空状态,电平不确定,树莓派就读不到数据。4.7kΩ是经过验证的经典值,太小了功耗大,太大了上升沿变缓影响通信。
具体接法:
- DS18B20的VCC接树莓派的3.3V(引脚1或17)
- DS18B20的GND接树莓派的GND(引脚6、9、14等)
- DS18B20的DQ接树莓派的GPIO4(引脚7),同时通过4.7kΩ电阻上拉到3.3V
我选GPIO4是因为它是树莓派默认的1-Wire引脚,配置起来最省事。当然你也可以用其他GPIO,但需要在配置文件里额外指定。
3.2 风扇驱动电路:MOS管三个引脚怎么接
AO3400是SOT-23封装,三个引脚分别是栅极(G)、源极(S)、漏极(D)。接法如下:
- 栅极(G):接树莓派的PWM引脚(我用的是GPIO18,引脚12),中间串一个1kΩ电阻
- 源极(S):接GND
- 漏极(D):接风扇的负极
- 风扇的正极:接树莓派的5V引脚(引脚2或4)
同时,在栅极和源极之间接一个10kΩ下拉电阻,确保没有信号时MOS管关断。
这里有个容易搞混的地方:风扇的负极接MOS管的漏极,不是直接接GND。MOS管在这里扮演的是一个"可控开关",栅极有电压时,漏极和源极之间导通,风扇的负极就相当于接到了GND,风扇转起来;栅极没电压时,漏极和源极断开,风扇负极悬空,风扇停转。
注意:如果你用的是12V风扇,风扇正极要接12V电源的正极,12V电源的GND要和树莓派的GND连在一起(共地),否则MOS管无法正常控制。这一点很多人会忽略,导致风扇要么不转要么一直转。
3.3 完整接线清单与检查要点
我把所有接线整理成一张表,方便你对照检查:
| 元件 | 引脚 | 接到树莓派 | 备注 |
|---|---|---|---|
| DS18B20 | VCC | 3.3V(引脚1) | 不要接5V |
| DS18B20 | GND | GND(引脚6) | |
| DS18B20 | DQ | GPIO4(引脚7) | 需4.7kΩ上拉到3.3V |
| AO3400 | 栅极G | GPIO18(引脚12) | 串1kΩ电阻 |
| AO3400 | 源极S | GND(引脚9) | |
| AO3400 | 漏极D | 风扇负极 | |
| 风扇 | 正极 | 5V(引脚2) | |
| 下拉电阻 | 一端 | 栅极G | 10kΩ |
| 下拉电阻 | 另一端 | GND |
接线完成后,先别急着上电。用万用表通断档检查一遍:3.3V和GND有没有短路、MOS管三个引脚有没有接反、上拉电阻和下拉电阻位置对不对。我见过太多人因为一根线接错,烧了传感器或者MOS管,返工重来很浪费时间。
4. 系统配置与代码实现:让温度真正驱动转速
4.1 启用1-Wire接口:两种配置方式的区别
树莓派读取DS18B20需要先启用1-Wire接口。有两种方式:
方式一:通过raspi-config图形界面
sudo raspi-config进入Interface Options->1-Wire-> 选择Yes-> 重启。这种方式最直观,适合新手。
方式二:手动修改配置文件
sudo nano /boot/config.txt在文件末尾添加一行:
dtoverlay=w1-gpio,gpiopin=4如果你用的不是GPIO4,把gpiopin=4改成你实际使用的引脚号。保存后重启。
重启后验证是否生效:
ls /sys/bus/w1/devices/如果看到类似28-xxxxxxxxxxxx的目录,说明DS18B20已经被识别。进入该目录读取温度:
cat /sys/bus/w1/devices/28-xxxxxxxxxxxx/w1_slave输出里会有一行t=28500,表示28.5℃。如果看到t=85000或者t=-127000,说明传感器没接好或者上拉电阻有问题。
4.2 用Python读取温度:为什么不用现成库
读取DS18B20的Python库有不少,比如w1thermsensor,用起来确实方便。但我更推荐直接读系统文件,原因有两个:一是少装一个依赖,系统更干净;二是能看清楚底层到底发生了什么,出问题好排查。
核心代码就几行:
def read_temp(): device_file = '/sys/bus/w1/devices/28-xxxxxxxxxxxx/w1_slave' with open(device_file, 'r') as f: lines = f.readlines() if lines[0].strip()[-3:] != 'YES': return None equals_pos = lines[1].find('t=') if equals_pos != -1: temp_string = lines[1][equals_pos+2:] temp_c = float(temp_string) / 1000.0 return temp_c return None这里有个细节:lines[0]的末尾如果是YES,表示CRC校验通过,数据可信;如果是NO,说明读取失败,应该丢弃这次数据。很多人直接读t=的值,不检查CRC,偶尔会读到错误温度,导致风扇莫名其妙狂转。
4.3 PWM调速:频率和占空比怎么定
树莓派的PWM可以通过RPi.GPIO库或者pigpio库实现。我用的RPi.GPIO,够用且简单。
import RPi.GPIO as GPIO FAN_PIN = 18 GPIO.setmode(GPIO.BCM) GPIO.setup(FAN_PIN, GPIO.OUT) pwm = GPIO.PWM(FAN_PIN, 100) # 频率100Hz pwm.start(0) # 初始占空比0频率为什么选100Hz?风扇电机的PWM频率一般建议在20Hz到200Hz之间。太低会有可闻的"哒哒"声,太高MOS管开关损耗增加,而且有些风扇在太高频率下反而转不动。100Hz是个比较稳妥的中间值,实测噪音和调速线性度都不错。
占空比怎么映射?我的做法是分段映射:
- 温度低于28℃:占空比0%,风扇停
- 温度28℃到35℃:占空比从30%线性增加到100%
- 温度高于35℃:占空比100%,全速
为什么低温不直接给个20%让它慢慢转?因为很多小风扇在低占空比下根本转不起来,会发出"嗡嗡"声但不转,反而更吵。30%是大多数5V小风扇的启动阈值,低于这个值不如直接停。
def temp_to_duty(temp): if temp < 28: return 0 elif temp > 35: return 100 else: return 30 + (temp - 28) * (100 - 30) / (35 - 28)4.4 主循环:采样间隔与迟滞设计
主循环的逻辑很简单:读温度、算占空比、设置PWM、等一会儿、重复。但有两个细节值得说。
采样间隔:DS18B20的转换时间默认是750ms,读太快没意义。我设的是2秒一次,既不会错过温度变化,也不会给CPU增加负担。
迟滞(Hysteresis):这是很多人忽略的一点。假设阈值是28℃,温度在28℃附近波动时,风扇会频繁启停,听起来像在"喘气"。解决办法是加一个迟滞区间:比如温度升到28℃才启动,但要降到26℃才停止。这样中间有2℃的缓冲,避免频繁切换。
fan_on = False while True: temp = read_temp() if temp is None: time.sleep(2) continue if not fan_on and temp >= 28: fan_on = True elif fan_on and temp <= 26: fan_on = False if fan_on: duty = temp_to_duty(temp) else: duty = 0 pwm.ChangeDutyCycle(duty) time.sleep(2)5. 调试过程中踩过的坑与排查思路
5.1 温度读出来是85℃或者-127℃
这是DS18B20最经典的故障。85℃是传感器上电后的默认值,表示它还没完成一次有效的温度转换;-127℃表示通信失败,树莓派根本没读到数据。
排查顺序:
- 检查接线是否松动,尤其是DQ线
- 检查4.7kΩ上拉电阻是否接好,阻值对不对
- 检查
/boot/config.txt里的dtoverlay配置是否正确 - 用
ls /sys/bus/w1/devices/看设备有没有被识别
我遇到过一次,折腾了半天发现是杜邦线接触不良,换了一根线就好了。所以别一上来就怀疑代码,先查硬件。
5.2 风扇不转或者一直全速转
风扇不转,先量MOS管栅极电压。如果GPIO输出高电平时栅极电压接近3.3V,说明控制信号没问题,问题在MOS管或者风扇本身;如果栅极电压一直是0,说明GPIO没输出,检查代码里PWM有没有启动。
风扇一直全速转,最常见的原因是MOS管接反了。AO3400的栅极、源极、漏极如果搞混,MOS管可能一直处于导通状态。对照数据手册确认引脚定义,别凭感觉接。
还有一个可能:下拉电阻没接或者阻值太大,栅极悬空时被干扰信号触发。10kΩ是标准值,别省。
5.3 PWM调速时风扇发出异响
如果风扇在某个占空比下发出"滋滋"声或者"哒哒"声,通常是PWM频率不合适。可以试试把频率从100Hz调到50Hz或者200Hz,看哪个更安静。不同风扇的电机特性不一样,没有万能值,得试。
另外,如果风扇在低占空比下抖动但不转,说明占空比低于它的启动阈值。把最低占空比从30%提到40%试试,或者干脆在低温时直接停转,别让它半死不活地抖。
5.4 树莓派重启后程序不自动运行
调试的时候手动跑Python脚本没问题,但重启后风扇不工作,这是因为程序没有设置开机自启。最简单的方式是用systemd服务:
sudo nano /etc/systemd/system/fancontrol.service写入:
[Unit] Description=Temperature Controlled Fan After=multi-user.target [Service] Type=simple ExecStart=/usr/bin/python3 /home/pi/fancontrol.py Restart=always [Install] WantedBy=multi-user.target然后启用:
sudo systemctl daemon-reload sudo systemctl enable fancontrol.service sudo systemctl start fancontrol.service用systemctl status fancontrol.service查看运行状态,有报错会直接显示出来,比盲猜快得多。
6. 几个让项目更耐用的实战经验
6.1 给温度读数加一层滑动平均滤波
DS18B20虽然精度不错,但偶尔还是会有跳变。如果直接用单次读数做判断,风扇可能会因为一个异常值突然狂转。我的做法是维护一个长度为5的队列,每次读到的温度放进去,取平均值作为控制依据。
from collections import deque temp_history = deque(maxlen=5) def get_filtered_temp(): temp = read_temp() if temp is not None: temp_history.append(temp) if len(temp_history) == 0: return None return sum(temp_history) / len(temp_history)这样即使有一次读数异常,对平均值的影响也只有五分之一,风扇不会一惊一乍。
6.2 风扇供电最好独立,别和树莓派抢电
我一开始图省事,风扇直接从树莓派的5V引脚取电。大部分时候没问题,但有一次风扇启动瞬间,树莓派直接重启了。原因就是启动电流太大,把5V电压拉低了,树莓派欠压保护触发。
后来我改成用独立的5V电源给风扇供电,树莓派的5V只用来给传感器供电,问题就再没出现过。如果你手头没有独立电源,至少要在树莓派的电源适配器上留足余量,别用那种只有1A的便宜货。
提示:树莓派官方推荐电源是5V 3A,如果你要带风扇,建议用5V 3A以上的电源,留出余量。
6.3 外壳和风道比风扇本身更重要
很多人花大价钱买高转速风扇,结果散热效果还是不好,问题往往出在风道上。风扇不是转得越快越好,关键是要让气流经过发热区域。如果风扇只是对着空气吹,再快也没用。
我的做法是用一个简单的亚克力板做个导风罩,把风扇的气流集中吹向发热源。成本几块钱,效果比换个更贵的风扇明显得多。另外,进风口和出风口要留够空间,别把风扇闷在密闭盒子里,那样只会把热空气搅来搅去。
6.4 日志记录能帮你发现很多隐藏问题
我在代码里加了一个简单的日志功能,每隔一段时间把温度、占空比、风扇状态写到一个文件里。平时不看,但出问题的时候翻一翻,能很快定位。
比如有一次风扇半夜突然狂转,我查日志发现那段时间温度读数一直在30℃以上,但白天正常。后来发现是设备晚上在跑一个定时任务,发热量确实变大了。如果没有日志,我可能一直以为是传感器故障。
import logging logging.basicConfig(filename='/home/pi/fan.log', level=logging.INFO, format='%(asctime)s - %(message)s') logging.info(f'Temp: {temp:.1f}C, Duty: {duty}%')日志不用写太频繁,每10次采样记一次就够了,不然文件涨得很快。
6.5 阈值不是拍脑袋定的,要实测
28℃启动、26℃停止,这个阈值是我根据自己设备的情况定的。你的设备可能不一样,别直接抄。正确的做法是:先让设备正常运行一段时间,记录温度变化范围,然后取一个略高于正常温度、又低于危险温度的值作为启动阈值。
比如设备正常运行时温度在25℃到27℃之间波动,那启动阈值可以定在29℃或30℃,停止阈值定在27℃。这样既不会频繁启停,又能在真正需要的时候及时散热。
7. 这套方案还能怎么扩展
温控风扇本身不复杂,但它是一个很好的"底座",可以往上加很多东西。
比如加一个OLED屏幕,实时显示当前温度和风扇转速,不用登录树莓派就能看到状态。OLED用I2C接口,接线简单,Python库也成熟。
再比如加一个手动/自动切换开关,有时候你想让风扇强制转或者强制停,按一下按钮就能切换模式。用一个轻触开关接GPIO,配合中断检测,几行代码就能实现。
如果设备不止一个发热点,可以挂多个DS18B20在同一根1-Wire总线上,分别监测不同位置的温度,取最高值作为控制依据。DS18B20的1-Wire总线支持多个设备共存,每个设备有唯一的64位ID,读取时根据ID区分就行。
再进阶一点,可以把温度数据通过MQTT发到一个本地服务器,用手机或者网页远程查看。这个就涉及到网络通信了,但树莓派跑这些毫无压力。
我自己的设备后来就加了个小屏幕,放在桌角,温度、转速一目了然。有时候调试其他东西,看一眼屏幕就知道设备热不热,比登录SSH方便多了。
整套东西做下来,硬件成本大概几十块钱,代码不到一百行,但实用性很强。更重要的是,通过这个项目你把GPIO输入输出、1-Wire通信、PWM调速、systemd服务这几个树莓派核心知识点都过了一遍,以后再遇到类似需求,基本就是换个传感器、改个阈值的事。