树莓派使用温控风扇散热
年初购入一个树莓派 4B后,立刻遇到了散热问题,毕竟性能提升和大内存都是有代价的。散热如果不解决好,降频就很难用了。不过好在现在风扇价格都很便宜,尝试使用开放四面的层叠板外壳 + 4mm 直流风扇后发现,风扇本身还算静音,但是外壳会有轻微共振,在夜间尤其吵闹。于是自然想到通过芯片温度来控制风扇启动。
硬件 & 原理
普通直流风扇是不支持通过信号触发启动、关断的,所以我们要做的就是加上一个简单的电子开关。材料:
- 5V 直流风扇一个,3mm 或 4mm,根据你机器外壳的开孔尺寸选择。注意最好买透明外壳的,有部分劣质塑料外壳会散发强烈刺激性气味。
- 2N2222 三极管一个
- 680Ω 电阻一个
- 一小块 PCB 和三孔(2.54mm 间距)的插座,用于焊接上面那些元件,并方便接入树莓派的 GPIO 引脚。如果没有的话,自己动脑想办法咯。
按下图连接,或者参考这一段。
基本原理就是,当将引脚 1
拉高时,三极管 Q1
会导通,风扇开始运转;反之则三极管断开,风扇停止。
因为我选择的是 GPIO 14 引脚作为控制线,相邻的 5V 和 GND 作为电源,因此后续代码都使用这个配置。
Python 版本
按照温度来控制一个 GPIO 引脚的高低是一个比较简单的工作,因此我最开始就是花两分钟写了个 Python 脚本然后安装成 systemd 服务来跑的。脚本大致是这样子:
|
|
使用 pip3 安装 gpiozero
/ loguru
/ vcgencmd
,然后直接运行这个脚本即可。
因为去年8月 Wiring Pi 作者宣布项目不再维护,所以这里没有使用 Wiring Pi 的 binding,而是使用的 gpiozero。此外,这里读取温度使用了 vcgencmd,它是通过开启子进程调用 vcgencmd measure_temp
来查询芯片温度的,这是我偏好的获取温度数据的方式。
不过这样一个简单的工作,交给 Python 脚本来做,需要占用 15MB 内存,空闲 CPU 占用 0.1%;并且通过子进程调用外部命令来查询温度十分不雅,因此我改为使用 C 编写相同的逻辑。
C 语言版本
源代码我发布在了这里。主要依赖 MRAA 库和 userland 仓库中与 VideoCore 芯片通信相关的内容。构建依赖 cmake 和 gcc。VideoCore 相关库和头文件在 Raspbian 系统上预装在 /opt/vc
,其他系统可能需要自行解决。
安装依赖
|
|
运行 mraa-gpio version
,如果打印出 MRAA 版本号和树莓派的型号信息,则说明安装完成。
编译安装 fanctl 服务
|
|
注意:如果要使用不同的 GPIO 引脚来控制风扇,请在编译之前修改 fanctl.c
文件中 FAN_VCC_PIN
的值。你可以在 Pinout! 查阅各个引脚的编号,注意 MRAA 库使用的是物理编号。
配置和启动服务
安装后,工具会被安装在 /usr/local/bin/fanctl
,systemd 服务配置在 /etc/systemd/system/fanctl.service
。在服务配置中 [Service]
段内,通过环境变量 THRESHOLD_ON
和 THRESHOLD_OFF
可以配置风扇启动和停止的温度(以摄氏度计)。例如以下配置表示在温度超过 48 摄氏度时启动,当温度降至 38 摄氏度时停止。
|
|
修改完成后,需要重新加载 systemd 服务配置才能启动服务:
|
|
如何读取温度
眼尖的读者可能已经发现了,Linux 下其实可以直接读取 /sys/class/thermal/thermal_zone0/temp
文件来获取当前 CPU 的温度。树莓派上 CPU 与 GPU 是同一颗芯片,并且这块芯片只有一个温度传感器,所以其实我们不需要使用 vcgencmd
命令,也不需要依赖 VideoCore 相关的库?
在树莓派官方文档的一个角落中是这样解释的:
Due to the architecture of the SoCs used on the Raspberry Pi range, and the use of the upstream temperature monitoring code in the Raspberry Pi OS distribution, Linux-based temperature measurements can be inaccurate. There is a command that can provide an accurate and instantaneous reading of the current SoC temperature, as it communicates with the GPU directly:
vcgencmd measure_temp
vcgencmd
是与 VideoCore 芯片进行直接通信的工具,因此 vcgencmd measure
的结果是最精确的,Linux 通用的温度测量方法则可能不是那么精确。