Automatic fan control on the OrangePi PC (or others Linux SBCs)

Hi,

Here is an overview of how I added automatic fan control to my OrangePi PC running Armbian Bionic.

The hardware

My OrangePi PC was already equipped with case and fan:

To control the fan I added a small transistor interrupting the fan ground wire with a base resistor to the cable, then connected it to one of the GPIO pins:

GPIO Control

The next step was figuring out how set/reset this pin, first i went to the OrangePi PC schematics and found which GPIO it was:

After searching for ways to control the GPIO and experiencing with some packages, I decided to do it directly accessing the kernel’s sysfs (although this is already deprecated, as stated here, it works and is the easiest way to access GPIOs from userspace/bash).

For using the sysfs, first we need to calculate the GPIO number based on this formula:

(position of letter in alphabet - 1) * 32 + pin number
(found here)
Which, for PD14, will give us: 3*32+14 = 110
Then we need to configure it:
echo 110 > /sys/class/gpio/export
echo out > /sys/class/gpio/gpio110/direction
And then we can control it with:

echo 1 > /sys/class/gpio/gpio110/value #for turning on
echo 0 > /sys/class/gpio/gpio110/value #for turning off

Reading the temperature

Reading the processor’s temperature on the Armbian Bionic is pretty straight, we just need to read the file “temp” from /sys/devices/virtual/thermal/thermal_zone0/, but it will give us the temperature in “millidegree Celsius”, which is way more than we need, so a easy solution is to just get the first 2 characters from it using cut:
cat /sys/devices/virtual/thermal/thermal_zone0/temp | cut -c 1-2

Putting all together in a script

Now that I already know how to deal with the GPIO and the temperature, I put everything in a script named fan_control.sh:

 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
#!/bin/bash

echo 110 > /sys/class/gpio/export
echo out > /sys/class/gpio/gpio110/direction

th_on=60
th_off=50

echo $(date)" FAN Control (1.0) >> ON: $th_on / OFF: $th_off"

clean_up() {

 # Perform program exit housekeeping
 echo 0 > /sys/class/gpio/gpio110/value
 echo 110 > /sys/class/gpio/unexport
 echo $(date)" FAN Control terminated."
 exit
}

trap clean_up INT TERM

while [ "1" = "1" ]
do
temp=`cat /sys/devices/virtual/thermal/thermal_zone0/temp | cut -c1-2`
state=`cat /sys/class/gpio/gpio110/value`

# echo "Temperature: $temp"
if [ $temp -ge $th_on ]
then
if [ $state = 0 ]
then
echo $(date)" Fan started: $temp"
echo 1 > /sys/class/gpio/gpio110/value
fi
elif [ $temp -le $th_off ]
then
if [ $state = 1 ]
then
echo $(date)" Fan stopped: $temp"
echo 0 > /sys/class/gpio/gpio110/value
fi
fi
sleep 2
done
In lines 11-20 we set a trap to be able to turn off the fan when the script is finished,  in lines 22-44 we run the main loop, collecting the FAN state and temperature (lines 24 and 25), checking if it is higher or lower both thresholds (lines 28 and 35), and only acting if the fan is not already turned of or off (lines 30-34 and 37-41), and after the iteration it goes in sleep for 2 seconds (line 43).
Does not forget to make the script executable with:

chmod +x fan_control.sh

Making it run automatically

For setting it to run on boot, first I needed to figure which initsystem my board was running, for finding it we can do:

1
sudo stat /proc/1/exe
Now we know we are dealing with systemd, in this case the best way to start our script on boot is setting it as a service, for doing it I created a fan.service file with the following content:
[Unit]
Description=Control of the CPU Fan

[Service]
Type=simple
ExecStart=/root/fan/fan_control.sh
Restart=always
RestartSec=30
SyslogIdentifier=FAN

[Install]
WantedBy=multi-user.target

This service descriptor is setting our script to start on boot and keep it always running, if the process dies for any reason the systemd will restart it after 30 seconds.

For registering this service, you will need to do the following steps:

# create a symbolic link of the service file inside systemd/system folder
ln -s /root/fan/fan.service /etc/systemd/system/fan.service
# Install/enable the service, this will make systemd already start it on the next boot
sudo systemctl enable fan.service
# (optional) start the service 
sudo systemctl start fan.service

# check the service status
systemctl status fan.service

This is an output of the status reading after installing and starting the service:

Testing it

If you want to test it the easiest way is stressing the CPU, for this, the following script can be used:

# start 4 jobs to use CPU and keep then in background
for i in 1 2 3 4 ; do nice -n 20 cat /dev/zero > /dev/null & done 

# kill all started jobs in background
for pid in $( jobs -p ) ; do kill -9 $pid ; done
(from here)

You also can find all files and a short guide in my github repository:
https://github.com/gustavolaureano/orangepi_fan_control

That’s it, see you next time 🙂

Pages used as research source:
https://www.digitalocean.com/community/tutorials/understanding-systemd-units-and-unit-files
https://gist.github.com/fcangialosi/45701b95f7436049a6390fecf3c9b8a1
http://linuxcommand.org/lc3_wss0150.php
http://hilite.me/