Skip to content

Commit 6e775b3

Browse files
authored
Initial commit
1 parent 4c8d835 commit 6e775b3

File tree

3 files changed

+236
-0
lines changed

3 files changed

+236
-0
lines changed

README.md

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
# pwm-gpio-fan overlay
2+
3+
Raspberry Pi device-tree overlay for a GPIO connected PWM cooling fan controlled by the software-based GPIO PWM kernel module
4+
5+
## Description
6+
7+
There are several PWM controlled cooling fans avaliable for the Raspberry Pis prior to the Pi 5, that are connected via the Pi's GPIO header.
8+
Examples are the Argon mini-fan, HighPi Pro Fan or Waveshare FAN-4020-PWM-5V.
9+
10+
What these fans have in common is, that they are using the +5V pin and GND pin from the Pi's GPIO header to supply power to the fan and an additional GPIO pin (mostly GPIO 18) to control the fan state to be either on or off.
11+
12+
### Available legacy solutions
13+
14+
#### 1. The gpio-fan overlay:
15+
The Raspberry Pi OS supports a simple kernel module controlled by the gpio-fan overlay to just switch the fan on or off at a specific temperature. This solution is available back from the legacy Buster OS version.
16+
- **Pro:** Built-in solution, even configurable from the raspi-config tool in Raspberry Pi OS Buster and Bullseye.
17+
- **Con:** Unfortunately these small fans tend to be quiet noisy when just switched on at full speed.
18+
19+
#### 2. 3rd party overlays to control the fan by the Pis built-in hardware PWMs:
20+
There are several implementations available on the internet to do this.
21+
- **Pro:** The fan remains much quieter, often not even noticable.
22+
- **Con:** These solutions require one of the two Pi's hardware PWMs (PWM0 or PWM1). But, both hardware PWMs are required by the Pi for analogue audio output, so analogue audio is heavily disturbed if you use a hardware PWM for fan control (if you like to try it, please use very low volume levels, its an ugly noise on the speakers or head phones).
23+
24+
#### 3. Userland scripts:
25+
There are several implementations available on the internet to do this. Mostly python scripts, sometimes combined with C code.
26+
- **Pro:** The fan remains much quieter, often not even noticable, and no hardware PWM is occupied, so no conflict with the Pi's analogue audio output.
27+
- **Con:** The scripts can be unreliable to control the fan at very high CPU loads. The scripts itself sometimes consume noticable CPU power to do their job. And finally, the python GPIO interfaces have changed substantially over time. There are still many scripts available, that depend on WiringPi and WiringPi is no longer part of the OS repositories since Bullseye in 2021.
28+
29+
### New solution by using software PWM
30+
31+
Fortunately a new kernel based software PWM solution is available since November 2024. A software-based PWM kernel module is available since then, back-ported from the Linux kernel 6.11 to the Raspberry Pi OS Bookworm kernel 6.6.62.
32+
33+
This made me write the **pwm-gpio-fan** overlay for my own use and publish it here for the community.
34+
35+
- **Pro:** The fan remains much quieter, often not even noticable, and no hardware PWM is occupied, so no conflict with the Pi's analogue audio output. Reliable on even high CPU loads, as it's part of the kernel. Doesn't consume noticable CPU power even on a Pi 3.
36+
37+
- **Con:** Available on Raspberry Pi OS Bookworm with kernel 6.6.62 or above only. Sorry no support for earlier OS or kernel versions.
38+
39+
### How to install
40+
41+
1. Download the file pwm-gpio-fan.dtbo from the repository
42+
2. Copy pwm-gpio-fan.dtbo to /boot/firmware/overlays/ on your Raspberry Pi
43+
3. Add one line to the end of /boot/firmware/config.txt: "dtoverlay=pwm-gpio-fan"
44+
4. Reboot
45+
46+
### Customization
47+
48+
There are several parameters you can specifity in the /boot/firmware/config.txt command line to customize the behaviour of the overlay:
49+
50+
- "fan_gpio" BCM number of the pin driving the fan, default 18 (GPIO 18)
51+
- "fan_temp0" CPU temperature at which fan is started with low speed in millicelsius, default 55000 (55 °C)
52+
- "fan_temp1" CPU temperature at which fan is switched to medium speed in millicelsius, default 60000 (60 °C)
53+
- "fan_temp2" CPU temperature at which fan is switched to high speed in millicelsius, default 67500 (67.5 °C)
54+
- "fan_temp3" CPU temperature at which fan is switched to max speed in millicelsius, default 75000 (75 °C)
55+
- "fan_temp0_hyst" Temperature hysteris at which fan is stopped in millicelsius, default 5000 (resulting in 50 °C)
56+
- "fan_temp1_hyst" Temperature hysteris at which fan is switched back to low speed in millicelsius, default 5000 (resulting in 55 °C)
57+
- "fan_temp2_hyst" Temperature hysteris at which fan is switched back to medium speed in millicelsius, default 5000 (resulting in 62.5 °C)
58+
- "fan_temp3_hyst" Temperature hysteris at which fan is switched back to high speed in millicelsius, default 5000 (resulting in 70 °C)
59+
- "fan_temp0_speed" Fan speed for low cooling state in range 0 to 255, default 114 (45% PWM duty cycle)
60+
- "fan_temp1_speed" Fan speed for medium cooling state in range 0 to 255, default 152 (60% PWM duty cycle)
61+
- "fan_temp2_speed" Fan speed for high cooling state in range 0 to 255, default 204 (80% PWM duty cycle)
62+
- "fan_temp3_speed" Fan speed for max cooling state in range 0 to 255, default 255 (100% PWM duty cycle)
63+
64+
> [!NOTE]
65+
> Lowest temperature default is set to 55 °C, to keep the fan off when the Pi is idle. PWM duty cycles start from a rather high 45% to ensure the fan is started reliable. Many small fans have difficulties to start reliable at low duty cycle values.
66+
67+
### How to compile the overlay from the provided source
68+
69+
The source code of the overlay is provided in the repository as **pwm-gpio-fan-overlay.dts** to allow you to make any modifications you like.
70+
71+
1. Download the file pwm-gpio-fan-overlay.dts from the repository
72+
2. Modify it to your needs
73+
3. Compile with the command "sudo dtc -I dts -O dtb -o /boot/firmware/overlays/pwm-gpiofan.dtbo pwm-gpiofan-overlay.dts"
74+
4. Add one line to the end of /boot/firmware/config.txt: "dtoverlay=pwm-gpio-fan"
75+
5. Reboot
76+
77+
### WARRANTY
78+
> [!IMPORTANT]
79+
> The device-tree overlay provided here is free software. It comes with ABSOLUTELY NO WARRANTY, to the extent permitted by applicable law.

pwm-gpio-fan-overlay.dts

Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
/*
2+
* Overlay for a GPIO connected PWM cooling fan controlled by software GPIO PWM
3+
*
4+
* Optional parameters:
5+
* - "fan_gpio" BCM number of the pin driving the fan, default 18 (GPIO18)
6+
* - "fan_temp0" CPU temperature at which fan is started with low speed in millicelsius, default 55000 (55 °C)
7+
* - "fan_temp1" CPU temperature at which fan is switched to medium speed in millicelsius, default 60000 (60 °C)
8+
* - "fan_temp2" CPU temperature at which fan is switched to high speed in millicelsius, default 67500 (67.5 °C)
9+
* - "fan_temp3" CPU temperature at which fan is switched to max speed in millicelsius, default 75000 (75 °C)
10+
* - "fan_temp0_hyst" Temperature hysteris at which fan is stopped in millicelsius, default 5000 (resulting in 50 °C)
11+
* - "fan_temp1_hyst" Temperature hysteris at which fan is switched back to low speed in millicelsius, default 5000 (resulting in 55 °C)
12+
* - "fan_temp2_hyst" Temperature hysteris at which fan is switched back to medium speed in millicelsius, default 5000 (resulting in 62.5 °C)
13+
* - "fan_temp3_hyst" Temperature hysteris at which fan is switched back to high speed in millicelsius, default 5000 (resulting in 70 °C)
14+
* - "fan_temp0_speed" Fan speed for low cooling state in range 0 to 255, default 114 (45% PWM duty cycle)
15+
* - "fan_temp1_speed" Fan speed for medium cooling state in range 0 to 255, default 152 (60% PWM duty cycle)
16+
* - "fan_temp2_speed" Fan speed for high cooling state in range 0 to 255, default 204 (80% PWM duty cycle)
17+
* - "fan_temp3_speed" Fan speed for max cooling state in range 0 to 255, default 255 (100% PWM duty cycle)
18+
*
19+
* N.B.
20+
* - Uses the software GPIO PWM kernel module instead of the Pis hardware PWMs (PWM0/PWM1).
21+
* This will allow for an undisturbed concurrent usage of the Pis analogue audio output.
22+
*
23+
* Requires:
24+
* - A PWM controlled cooling fan connected to the GPIO, such as an
25+
* Argon mini-fan, HighPi Pro Fan or Waveshare FAN-4020-PWM-5V
26+
* - Raspberry Pi OS Bookworm with kernel 6.6.62 or above
27+
*
28+
* Build:
29+
* - sudo dtc -I dts -O dtb -o /boot/firmware/overlays/pwm-gpiofan.dtbo pwm-gpiofan-overlay.dts
30+
*
31+
* Activate:
32+
* - sudo nano /boot/firmware/config.txt add "dtoverlay=pwm-gpiofan"
33+
*
34+
*/
35+
/dts-v1/;
36+
/plugin/;
37+
38+
/ {
39+
compatible = "brcm,bcm2835";
40+
41+
fragment@0 {
42+
target = <&gpio>;
43+
__overlay__ {
44+
pwm_gpio_pins: pwm_gpio_pins {
45+
brcm,pins = <18>; /* gpio-pin = 18 */
46+
brcm,function = <1>; /* gpio function = output */
47+
brcm,pull = <0>; /* gpio pull up/down = off */
48+
};
49+
};
50+
};
51+
52+
fragment@1 {
53+
target-path = "/";
54+
__overlay__ {
55+
pwm_gpio: pwm_gpio {
56+
compatible="pwm-gpio";
57+
#pwm-cells = <2>;
58+
pinctrl-names = "default";
59+
pinctrl-0 = <&pwm_gpio_pins>;
60+
gpios = <&gpio 18 0>; /* gpio-pin = 18 */
61+
};
62+
};
63+
};
64+
65+
fragment@2 {
66+
target-path = "/";
67+
__overlay__ {
68+
fan0: pwm-fan {
69+
compatible = "pwm-fan";
70+
#cooling-cells = <2>;
71+
/* in ns = 20ms = 50 Hz */
72+
pwms = <&pwm_gpio 0 20000000 0>;
73+
74+
cooling-min-state = <0>;
75+
cooling-max-state = <4>;
76+
/* PWM duty cycle values in a range from 0 to 255
77+
which correspond to thermal cooling states 0 to 4 */
78+
cooling-levels = <0 114 152 204 255>;
79+
};
80+
};
81+
};
82+
83+
fragment@3 {
84+
target = <&cpu_thermal>;
85+
__overlay__ {
86+
/* in ms = poll every 2s */
87+
polling-delay = <2000>;
88+
};
89+
};
90+
91+
fragment@4 {
92+
target = <&thermal_trips>;
93+
__overlay__ {
94+
/* below temperatures in millicelsius */
95+
trip0: trip0 {
96+
temperature = <55000>; /* 55 °C */
97+
hysteresis = <5000>; /* 5 °C */
98+
type = "active";
99+
};
100+
trip1: trip1 {
101+
temperature = <60000>; /* 60 °C */
102+
hysteresis = <5000>; /* 5 °C */
103+
type = "active";
104+
};
105+
trip2: trip2 {
106+
temperature = <67500>; /* 67.5 °C */
107+
hysteresis = <5000>; /* 5 °C */
108+
type = "active";
109+
};
110+
trip3: trip3 {
111+
temperature = <75000>; /* 75 °C */
112+
hysteresis = <5000>; /* 5 °C */
113+
type = "active";
114+
};
115+
};
116+
};
117+
118+
fragment@5 {
119+
target = <&cooling_maps>;
120+
__overlay__ {
121+
map0 {
122+
cooling-device = <&fan0 0 1>;
123+
trip = <&trip0>;
124+
};
125+
map1 {
126+
cooling-device = <&fan0 1 2>;
127+
trip = <&trip1>;
128+
};
129+
map2 {
130+
cooling-device = <&fan0 2 3>;
131+
trip = <&trip2>;
132+
};
133+
map3 {
134+
cooling-device = <&fan0 3 4>;
135+
trip = <&trip3>;
136+
};
137+
};
138+
};
139+
140+
__overrides__ {
141+
fan_gpio = <&pwm_gpio>,"gpios:4",
142+
<&pwm_gpio_pins>,"brcm,pins:0";
143+
fan_temp0 = <&trip0>,"temperature:0";
144+
fan_temp0_hyst = <&trip0>,"hysteresis:0";
145+
fan_temp0_speed = <&fan0>,"cooling-levels:4";
146+
fan_temp1 = <&trip1>,"temperature:0";
147+
fan_temp1_hyst = <&trip1>,"hysteresis:0";
148+
fan_temp1_speed = <&fan0>,"cooling-levels:8";
149+
fan_temp2 = <&trip2>,"temperature:0";
150+
fan_temp2_hyst = <&trip2>,"hysteresis:0";
151+
fan_temp2_speed = <&fan0>,"cooling-levels:12";
152+
fan_temp3 = <&trip3>,"temperature:0";
153+
fan_temp3_hyst = <&trip3>,"hysteresis:0";
154+
fan_temp3_speed = <&fan0>,"cooling-levels:16";
155+
};
156+
157+
};

pwm-gpio-fan.dtbo

3.09 KB
Binary file not shown.

0 commit comments

Comments
 (0)