0%

树莓派自动温控风扇

某天忘记把树莓派的电源断掉,然后第二天早上被它的风扇声音吵醒!!
严格来说在温度不高的时候风扇没必要打开,好在树莓派最不怕的就是各种折腾了。
动手把风扇改成温控的。

描述

raspbian的/sys/class/thermal/thermal_zone0/temp文件内存着CPU的温度,读出来处以1000就得到温度了,省了给它测温的麻烦,大赞👍。
另外需要用的一个三极管,我这里用的是S9013,网上很多教程用的是S8085。严格来说不同的三极管实现起来是有不同的,这里不深究哪种三极管更好了。
突然想复习一下三极管怎么工作的( ╯□╰ )

预备知识

首先要知道的是树莓派的引脚定义,将树莓派有网络接口的一端朝下,有GPIO接口(就是那四十个脚)的一面面向自己,从上到下,从左到右,为物理引脚1~40,像这样

1
2
3
4
1 2
3 4
5 6
···

但是具体到程序内,用的不是物理引脚,不同的库对引脚的定义也不同,这里提供一个对照图

本文将使用winringPi作为控制引脚的库。
可以使用命令

1
2
sudo apt install wiringpi
sudo apt purge wiringpi

来完成安装,可以使用

1
gpio -v

来验证。
目前没有查询到每个脚具体的电气参数,理论上是可以将风扇直接插在引脚上,然后控制引脚的电平来间接控制风扇的开关的,但是不清楚具体哪个脚能承受多大的电流,就没有贸然尝试了。用了比较保险的三极管。
将三极管平的一面面向自己,引脚向下,从左到右引脚分别是E(发射极Emitter)B(基极Basic)C(集电极collector)。
连接示意图参考如下

安装

按图连接好之后,安装控制软件了。

1
2
3
4
git clone https://github.com/BDZNH/AutoControlRaspberryFan.git
cd AutoControlRaspberryFan
make
sudo make install

可以前往https://github.com/BDZNH/AutoControlRaspberryFan查看帮助。

源代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
/******************************************************************************


-------------------------------------------------------------------------------
Project Name : AutoControlRaspberryfan
Author : BDZNH
Project URL : https://github.com/BDZNH/AutoControlRaspberryFan
what is this : Auto control raspberry fan with 5V. Turn the fan
when the temperaure is high than 45°C, turn off
fan when the CPU temperature is lower than 39°C.
-------------------------------------------------------------------------------



******************************************************************************/




#include <iostream>
#include <fstream>
#include <unistd.h>
#include <wiringPi.h>
#include <softPwm.h>
#include <ctime>
#define TEMP_PATH "/sys/class/thermal/thermal_zone0/temp"
#define LOG_PATH "/tmp/RaspberrypiFanSpeed.log"
#define PID_PATH "/var/run/autocontrolfan.pid"
#define _FANPIN 8
#define min(x,y) (x<=y?x:y)
using namespace std;

void GetCpuTempera(ifstream &fin,double &temp);
int initWiringPi();
void showInfo();
void SaveLog(ofstream &log, double &temp, int FanSpeed, time_t &time_cur);

int main()
{
showInfo();
ofstream log(LOG_PATH);
if (!log.is_open())
{
cout << "Can't open file : " << LOG_PATH << endl;
}
log.close();
ifstream fin(TEMP_PATH, ios_base::in);
if (!fin.is_open())
{
cout << "Can't open file : " << TEMP_PATH << endl;
return -1;
}
ofstream pid(PID_PATH);
if (!pid.is_open())
{
cout << "Can't open file : " << PID_PATH << endl;
}
pid << getpid() << endl;
pid.close();
pid.clear();
time_t time_cur;
double temp = 0;
int Fan_Speed = 0;
bool Forty_five_Flag = false;
if (initWiringPi() < 0)
{
return -1;
}

while (true)
{
GetCpuTempera(fin,temp);
if (temp >= 42)
cout << "Cpu temperature is : \033[0;31m" << temp << "°C \033[0m" << flush;
else
cout << "Cpu temperature is : \033[1;32m" << temp << "°C \033[0m" << flush;
if (Forty_five_Flag)
{
if (temp < 39.0)
{
Forty_five_Flag = false;
Fan_Speed = 0;
softPwmWrite(_FANPIN, Fan_Speed);
SaveLog(log, temp, Fan_Speed, time_cur);
}
else
{
SaveLog(log, temp, Fan_Speed, time_cur);
}
sleep(1);
}
else
{
if (temp < 39.0)
{
sleep(1);
Fan_Speed = 0;
softPwmWrite(_FANPIN, Fan_Speed);
SaveLog(log, temp, Fan_Speed, time_cur);
}
else if (temp >= 40.0 && temp <= 45.0)
{
Fan_Speed = min(((((int)temp - 40) * 10) + 60),100);
softPwmWrite(_FANPIN, Fan_Speed);
SaveLog(log, temp, Fan_Speed, time_cur);
sleep(2);
}
else if (temp > 45.0)
{
Fan_Speed = 100;
softPwmWrite(_FANPIN, Fan_Speed);
Forty_five_Flag = true;
SaveLog(log, temp, Fan_Speed, time_cur);
sleep(5);
}
}
sleep(1);
cout << "\r";
}
return 0;
}

void GetCpuTempera(ifstream &fin, double &temp)
{
fin >> temp;
temp = temp / 1000.0;
fin.clear();
fin.seekg(0, ios::beg);
}

int initWiringPi()
{
if (wiringPiSetup() != 0)
{
cout << "WiringPi setup failed" << flush << " \r";
return -1;
}
if (softPwmCreate(_FANPIN, 0, 100) != 0)
{
cout << "softPwmcreat setup failed" << flush << " \r";
return -2;
}
return 0;
}



void showInfo()
{
cout << "-------------------------------------------------------------------------------" << endl;
cout << " Project Name : AutoControlRaspberryfan " << endl;
cout << " Author : BDZNH " << endl;
cout << " Project URL : https://github.com/BDZNH/AutoControlRaspberryFan " << endl;
cout << " what is this : Auto control raspberry fan with 5V. Turn the fan " << endl;
cout << " when the temperaure is high than 45°C, turn off " << endl;
cout << " fan when the CPU temperature is lower than 39°C. " << endl;
cout << "-------------------------------------------------------------------------------" << endl;
cout << "\n\n\n" << endl;
}

void SaveLog(ofstream &log, double &temp, int Fan_Speed, time_t &time_cur)
{
log.open(LOG_PATH,ios_base::out);
if (!log.is_open())
{
cout << "Can't open file : " << LOG_PATH << endl;
}
time(&time_cur);
log << ctime(&time_cur) << "CPU temperature is : " << temp << "°C \nSet fan speed to " << Fan_Speed << endl;
log.close();
log.clear();
}

代码内设定的是超过45°C时开启风扇,低于39°C时关闭风扇。处于[40°C,45°C]时按需调节,温度越高,转速越快。
后边会让程序支持指定阈值和工作模式,不知道是不是我的错觉,不是满速旋转的时候风扇有怪怪的声音。

生命重在折腾


参考资料

https://blog.newnius.com/raspberry-control-fan-with-transistor.html
树莓派wiringPi库详解
PS:链接一内的脚本用Python实现,无奈Python会占用大量CPU,对树莓派来说并不合适。后来虽然在百度也找到一些python脚本,占用资源也很少,但是还是写了这个练手。

百度贴吧找到的python脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#!/usr/bin/env python
# encoding: utf-8

import RPi.GPIO
import time
RPi.GPIO.setwarnings(False)
RPi.GPIO.setmode(RPi.GPIO.BCM)
RPi.GPIO.setup(2, RPi.GPIO.OUT)
pwm = RPi.GPIO.PWM(2,100)
RPi.GPIO.setwarnings(False)


speed = 0
prv_temp = 0

try:


while True:

tmpFile = open( '/sys/class/thermal/thermal_zone0/temp' )
cpu_temp = int(tmpFile.read())
tmpFile.close()
if cpu_temp>=34500 :

if prv_temp<34500 :
#启动时防止风扇卡死先全功率转0.1秒
pwm.start(0)
pwm.ChangeDutyCycle(100)
time.sleep(.1)
speed = min( cpu_temp/125-257 , 100 )
pwm.ChangeDutyCycle(speed)
else :
pwm.stop()
prv_temp = cpu_temp

time.sleep(5)

except KeyboardInterrupt:
pass
pwm.stop()