0 更新记录
- 2024-07-11:初始版本
- 2024-07-15:添加TL;DR,删除了文中的长代码,完整代码上传到Github
最近要用到吃灰已久的树莓派4B,发现原来的TF卡已经损坏。更换TF卡写入最新64bit的RaspiOS-Lite,配置时发现之前使用的温控风扇的程序文件也找不到了,索性直接重新整理一下。
买外壳时自带了一个风扇,是普通的不支持PWM调速的5V风扇,只需要用程序根据CPU温度控制风扇启停即可。
TL;DR
完整代码已经发布在Github仓库pi_auto_fan
控制程序及设置控制程序自启动的步骤略繁琐,使用Makefile
把需要操作的步骤打包成了install
和uninstall
,直接运行sudo make insatll
即可完成编译、创建系统服务、启动服务、设置自启动,注意必须使用sudo
,因为创建系统服务需要使用root
权限。
运行sudo make install
之前仍然需要按照文章中编译测试部分的步骤安装依赖。
默认使用GPIO 14作为控制IO(fan_pin=14
),上限阈值为60°(temp_high=60.0
),下限阈值为50°(temp_low=50.0
),如需修改需要在文件auto_fan.c
中修改对应代码。
1 | git clone https://github.com/gsh1209/pi_auto_fan.git |
查看GPIO状态的一些命令
1 | raspi-gpio get | grep "GPIO 14" |
1 控制电路
找到了之前画的驱动电路,使用了两个三极管(型号均为S8050)级联来进一步减轻GPIO输出电流的负担。电路很简单,不过当时还在嘉立创白嫖了一块PCB,PCB源文件已经找不到了,只能找到一个布局的图片。
这个电路是经过长时间验证的,曾经用树莓派跑了大半年的局域网打印机共享服务器和SMB文件共享服务器,使用的散热方案就是这套电路,长时间工作时三极管也不会发热。
树莓派上的控制IO使用的是GPIO 14,紧挨着5V电源和GND,可以方便的使用3PIN的杜邦线进行连接,不过GPIO 14其实也是UART串口的TXD,如果需要用到串口可以使用其他IO作为风扇控制IO。
GPIO定义图片来自树莓派文档
2 控制程序
之前的程序使用的是Python来控制,但是由于控制程序需要长期运行,Python脚本占用的系统资源更高一些,尤其对于我的1GB内存版本有些不友好,这次使用C语言来写控制程序。
2.1 CPU温度读取
树莓派系统中提供了可供读取的文件来获取CPU温度,文件位于/sys/class/thermal/thermal_zone0/temp
,文件内容是整数形式的CPU温度,包含三位小数。如果需要将温度输出到终端显示需要将获取的数值除以1000进行转换。
1 | printf("CPU temperature: %.2f°C\n", atof(cpu_temp_str) / 1000.0); |
这里的温控风扇的程序运行在后台,不需要向终端显示温度,为了简单我暂时也不需要打印log,所以这里的获取温度的函数不做转换直接返回文件内容转换为整数。
1 | /** |
2.2 启停控制逻辑
控制程序每2s读取一次温度,和温度阈值进行比较,控制风扇的启停。为了防止风扇在阈值附近反复的开启和关闭,控制程序设置了上限阈值temp_high
和下限阈值temp_low
两个阈值,当温度超过上限时风扇启动,当温度低于下限时风扇停止。程序中设置上下限阈值为60℃和50℃,可根据环境温度等情况按需调整。
上下限阈值是直接写到程序代码中的,如果需要修改则要重新编译,合理的做法是作为参数传入或者使用其他方式配置,包括用于控制的GPIO也是这样,但是因为懒暂时就不写了。
1 | float temp_high = 60.0; |
2.3 编译测试
在编译程序之前,需要确保已经安装需要的依赖
1 | sudo apt update && sudo apt install build-essential git -y |
还需要安装GPIO控制库WiringPi,我使用源码编译安装,也可以直接下载Releases中编译好的二进制包安装。
1 | git clone https://github.com/WiringPi/WiringPi.git |
使用gpio readall
命令来验证WiringPi
是否安装成功。
编译时需要添加-l
参数链接WiringPi
库:
1 | gcc auto_fan.c -o auto_fan -lwiringPi |
使用sysbench
命令让CPU快速升温,测试风扇能否按照预期工作,sysbench
需要安装。
1 | sudo apt update && sudo apt install sysbench -y |
测试确保控制程序auto_fan
功能正常后,按照Linux系统中文件的组织习惯将其复制到/usr/bin
目录。
1 | sudo cp auto_fan /usr/bin |
3 设置控制程序自动启动
在树莓派中设置程序自动启动和一般Linux系统中的方法类似,如使用定时任务crontab
的@reboot
,将运行程序的命令写入rc.local
等,这里使用systemd
自定义系统服务的方法来实现自动启动控制程序。
3.1 创建系统服务
systemctl的服务配置文件存放在目录/usr/lib/systemd
中,有系统system和用户user之分,需要开机不登录就能运行的程序,存放在系统服务中即/usr/lib/systemd/system
目录下。风扇控制程序应当创建为系统服务,在/usr/lib/systemd/system
目录新建一个auto_fan.service
配置文件,需要使用sudo
,内容如下:
1 | [Unit] |
其中关键的配置项有ExecStart=/usr/bin/auto_fan &
和KillMode=mixed
:
ExecStart
表示服务启动时运行的命令,这里在后台(使用参数&
)运行编译后的风扇控制程序,参考man systemd.service
。KillMode
表示服务终止时的操作,设置为mixed
表示主进程将收到SIGTERM
信号,子进程收到SIGKILL
信号,参考man systemd.kill
。
设置KillMode
的目的是在服务停止时需要恢复GPIO的状态,尤其是如果此时风扇已被打开,服务停止风扇应当被立即关闭,这里通过在程序中接收SIGTERM
信号,并自定义信号处理函数的方式实现服务停止时,程序退出前恢复GPIO为默认状态。
还可以使用ExecStop=
指定一个服务停止时运行的命令来实现恢复GPIO状态这个功能,命令既可以使用系统提供的raspi-gpio
,也可以使用由WiringPi
库提供的gpio
命令,还可以使用C语言单独写一个实现释放GPIO的程序。
根据树莓派4B的文档和BCM2711的Datasheet(GPIO_PUP_PDN_CNTRL_REG0 Register),树莓派GPIO 14复位后的默认状态为:输入,内部下拉。
3.2 设置服务开机自动启动
使用命令设置服务为enable
即可开机自启动
1 | sudo systemctl enable auto_fan.service |
3.3 systemctl常用命令
- 启动、停止、重启服务
1 | sudo systemctl start/stop/restart auto_fan.service |
- 开启、关闭开机自启
1 | sudo systemctl enable/disable auto_fan.service |
- 查看运行状态
1 | sudo systemctl status auto_fan.service |
- 重载配置文件(服务启动时修改了service文件,需要重载配置)
1 | sudo systemctl daemon-reload |
4 其他
最后对比了一下Python版本和C版本的程序,在CPU占用上没什么差别。在内存占用上的差距也没有想象中那么大:
1 | # Python程序的pid 3669 |
使用的Python程序
1 | import time |