首先我不是EE(电子电路工程师),所以对电路一窍不通,但人就是这样,越菜越爱玩。所以如果你是EE的话,不要被我的蠢到(不要骂我蠢)。

我的家里就好几个树莓派,一些是我大学的时候做小任务的低功耗服务器用,另外一些是工作的时候,自己测试一些硬件用,跑一些边缘服务之类的。

树莓派旧风扇长期通电运行报废

我大学期间给树莓派 3B 配备的风扇在通电时间超过三年之后还是在最近寿终正寝了(作为一个4块钱的风扇而言,已经很抗造了),于是我就在想,为什么不做一个根据 CPU 温度动态控制风扇转速的模块,来延长风扇的寿命呢?说干就干。

众所周知,树莓派最新的 5B 系列已经有了成熟的风扇控制接口了。

图片来自:树莓派官网

网络上风扇开关电路简单明确,但不够优雅简洁

但我手里的好几个 3B 和 4B 这两个系列都没有呀,我也想要一个更加优雅的、模块化的接口,而不是临时的像这种:

基础原理图,来自 shumeipai.nxez.com

CSDN_luzze__123.jpeg

图片来自:CSDN 用户 luzze_123

图片来自:CSDN 用户 qq_40251961

PCB 速成,嘉立创启动!

但我不会画电路板,更不了解各种电子电路相关的知识,只好去折磨 AI 了 😄。

但其实每个人都是有一定的电路基础的,正负极、开关。所以我选择了更加速成的方式:
【保姆级】二十分钟零基础PCB绘制打样一条龙教程(立创EDA专业版)

只需要按照这个视频教程,你就可以在半小时左右学会最基础的 PCB 板的绘制技巧,我也是顺利地完成了第一版绘制

第一版电路图


风扇控制开关电路

如果你是比较懂电路的话,你肯定已经看到问题所在了,没错,我把三极管放在了 5V 正极了,导致板子到手后风扇的转速非常慢,有时候甚至转不起来,即使已经通过 GPIO 把电路导通了,风扇还是不转或者转的非常慢,我就不瞎分析了,放出 AI 的分析:

把 8050 放在正极(高侧)当“开关”用了 —— 8050 是 NPN 晶体管,做高侧开关时它会工作在发射跟随(emitter-follower)模式,发射极电压 ≈ 基极电压 − 0.7V。GPIO 给的是 3.3V,发射极最多 ~2.6V,风扇的电源是 5V → 风扇只拿到 ~2.6V,自然转得很慢。

我理解下来就是:三极管放正极会对电压电流有较大损耗,导致风扇工作所需功率不足。

第二版电路图

于是重新去嘉立创领券,重新打板,重新焊接电路,重新测试,可以了!!!

再配合 Python 脚本,和亿点点配置,一个全自动的风扇控制模块就做好了!

安装效果图

模块大小示意(对比 Type-C 接口)

总花销:

  • 买电子元器件(S8050、排针、排母):9元

  • 电烙铁基础套装(松香、焊锡丝、锡棉,支架)36.9元

然而这一趟折腾下来,我发现花销已经足够我买好多小风扇了,好在电烙铁可以反复使用,电子元器件也只消耗了一小部份,以后折腾其他东西也能用~

解除 GPIO 14 的串口占用,用来当做开关信号

当前这个模块占地较小,但也有代价,它使用的是GPIO 14进行电路开关控制,GPIO 14 在树莓派上原本是被串口通信功能占用,如果你跟我一样不使用串口功能,可以这样关闭掉:
sudo raspi-config

Interface Options → Serial Port

  • Would you like a login shell to be accessible over serial?No

  • Would you like the serial port hardware to be enabled?No

完成后退出并选择 Finish,允许它重启。

重启后:

sudo vim /boot/firmware/config.txt

确认或添加以下内容(顺序重要):

# 禁用蓝牙,释放 UART
dtoverlay=disable-bt

# 确保主 UART 关闭
enable_uart=0
sudo systemctl disable hciuart.service
sudo systemctl mask hciuart.service
sudo reboot

验证 GPIO 14 状态:

pinctrl get 14

应显示:

GPIO 14: level=0 func=INPUT

(或类似 function INPUT),没有出现 ALT0 / TXD0 / RXD0

此时即可用作普通 GPIO。

如果你需要串口功能,则可以使用另一种方案代替

这是另一个版本,它不占用串口功能,使用 GPIO 7 进行开关控制,最终组件也会相对应长一些

风扇控制脚本

Python 脚本:

import RPi.GPIO as GPIO
import time
import signal
import sys

# 控制风扇的GPIO(BCM编号)
FAN_GPIO = 14

# 温度阈值(摄氏度)
HIGH_TEMP = 45.0  # 高于此温度开启风扇
LOW_TEMP = 42.0   # 低于此温度关闭风扇

# 检查间隔(秒)
CHECK_INTERVAL = 5

# 设置GPIO模式
GPIO.setmode(GPIO.BCM)
GPIO.setup(FAN_GPIO, GPIO.OUT)

# 初始状态关闭风扇
GPIO.output(FAN_GPIO, GPIO.HIGH)
fan_status = False

# 获取CPU温度的函数
def get_cpu_temperature():
    try:
        with open('/sys/class/thermal/thermal_zone0/temp', 'r') as f:
            temp = float(f.read()) / 1000.0
        return temp
    except Exception as e:
        print(f"无法读取CPU温度: {e}")
        return None

# 控制风扇的函数
def control_fan(temperature):
    global fan_status
    
    if temperature >= HIGH_TEMP and not fan_status:
        GPIO.output(FAN_GPIO, GPIO.HIGH)  # 开启风扇
        fan_status = True
        print(f"温度: {temperature}°C - 风扇开启")
    elif temperature <= LOW_TEMP and fan_status:
        GPIO.output(FAN_GPIO, GPIO.LOW)  # 关闭风扇
        fan_status = False
        print(f"温度: {temperature}°C - 风扇关闭")
    else:
        status = "开启" if fan_status else "关闭"
        print(f"温度: {temperature}°C - 风扇保持{status}")

# 优雅退出处理
def signal_handler(sig, frame):
    print("\n正在关闭程序...")
    GPIO.output(FAN_GPIO, GPIO.LOW)  # 确保风扇关闭
    GPIO.cleanup()  # 清理GPIO资源
    print("风扇已关闭,程序退出")
    sys.exit(0)

# 注册信号处理
signal.signal(signal.SIGINT, signal_handler)
signal.signal(signal.SIGTERM, signal_handler)

# 主循环
print("开始监控CPU温度并控制风扇...")
print(f"温度阈值: 高于{HIGH_TEMP}°C开启, 低于{LOW_TEMP}°C关闭")
print("按 Ctrl+C 退出程序")

try:
    while True:
        temperature = get_cpu_temperature()
        if temperature is not None:
            control_fan(temperature)
        time.sleep(CHECK_INTERVAL)
except KeyboardInterrupt:
    signal_handler(None, None)
except Exception as e:
    print(f"程序运行出错: {e}")
    GPIO.output(FAN_GPIO, GPIO.LOW)
    GPIO.cleanup()

自动化服务配置

[Unit]

Description=Fan control Service

After=network.target

[Service]

Type=simple

WorkingDirectory= # 这里替换成python脚本所在目录

ExecStart=/usr/bin/python3 # 这里替换成python脚本的绝对路径

Restart=always

RestartSec=10

StandardOutput=syslog

StandardError=syslog

SyslogIdentifier=fan-control

# 环境变量(如果需要)

Environment=PYTHONUNBUFFERED=1

[Install]

WantedBy=multi-user.target

把上边这个脚本保存到

/etc/systemd/system/fan-control.service

然后依次执行:

重新加载服务文件

sudo systemctl daemon-reload

启动风扇控制脚本服务

sudo systemctl start fan-control.service

检查运行状态

sudo systemctl status fan-control.service

设置服务开机自启动

sudo systemctl enable fan-control.service

如果发现服务启动异常,可以通过这个命令来查看异常原因

sudo journalctl -u fan-control.service -n 50

效果

Oct 16 13:23:42 raspberrypi fan-control[2004]: 温度: 44.008°C - 风扇保持关闭
Oct 16 13:23:47 raspberrypi fan-control[2004]: 温度: 44.546°C - 风扇保持关闭
Oct 16 13:23:52 raspberrypi fan-control[2004]: 温度: 45.084°C - 风扇开启
Oct 16 13:23:57 raspberrypi fan-control[2004]: 温度: 45.084°C - 风扇保持开启
Oct 16 13:24:02 raspberrypi fan-control[2004]: 温度: 44.008°C - 风扇保持开启
Oct 16 13:24:07 raspberrypi fan-control[2004]: 温度: 43.47°C - 风扇保持开启
Oct 16 13:24:12 raspberrypi fan-control[2004]: 温度: 43.47°C - 风扇保持开启
Oct 16 13:24:17 raspberrypi fan-control[2004]: 温度: 42.932°C - 风扇保持开启
Oct 16 13:24:22 raspberrypi fan-control[2004]: 温度: 41.856°C - 风扇关闭
Oct 16 13:24:27 raspberrypi fan-control[2004]: 温度: 41.856°C - 风扇保持关闭
Oct 16 13:24:32 raspberrypi fan-control[2004]: 温度: 41.856°C - 风扇保持关闭
Oct 16 13:24:37 raspberrypi fan-control[2004]: 温度: 41.318°C - 风扇保持关闭

备注

  • 嘉立创领券链接,感谢嘉立创!😄

  • 我跑通了这个PCB验证,也写完了这篇文章后,才发现很久之前(2021年)就有其他人做过:可以参考「夸克之书」的文章,哎,早点儿看到就能少走很多弯路了。