But wait... what's I2C, what's PWM, and what's so special about a 4-pin fan connector? I'm glad you asked—this post will answer that and show you how you can control a fan connected to the IO Board, like the quiet Noctua NF-P12 pictured above with my IO Board.
If you plug a fan like that into the CM4 IO Board, it will start running full blast, 24x7. If you need that much cooling, that's great, but a lot of times, I don't mind my Pi's CPU getting warmer if it means I can run the fan silent most of the time.
So what are my options? First of all, I could just buy an inline PWM controller, like a Noctua NA-FC1. It lets me turn up and down the fan speed with a little dial. But it doesn't know the temperature of my Pi, so it can't increase airflow for higher temperatures or turn off the fan when it's under a certain temperature.
The better option is to use the built-in PWM fan controller on the IO Board (pictured above). And to do that, we're going to need to use the Raspberry Pi's I2C bus!
What is I2C?
I2C—or more correctly, I2C—stands for "Inter-Integrated Circuit" and is a two-wire serial communication interface used by many electronic devices for control and communications.
I'm not going to cover it in detail here, but if you get into any more advanced electronics projects with Arduino, Raspberry Pi, or other microcontrollers or PCs, you'll probably encounter it. To learn the basics of the protocol, I recommend Analog Device's I2C Primer.
Controlling the fan over I2C
You have to edit your
/boot/config.txt file to enable the
i2c_vc bus, which is bus #1. The Pi Device Tree Documentation actually recommends against touching
i2c_vc unless you need to, because you could mess up CSI camera or DSI display functionality.
Make sure the following lines exist and are uncommented in
/boot/config.txt and reboot the Pi:
# Enable I2C. dtparam=i2c_arm=on # Enable I2C bus 1. dtparam=i2c_vc=on
Note: If you just enable I2C under the 'Interfaces' option of
raspi-config, it will only enable
i2c_arm. To see the fan controller, you need to enable
Make sure the
i2c-tools package is installed on your system; if it is, the following commands should work straightaway. If not, you will need to install the package with
sudo apt-get install -y i2c-tools.
Now, check if you can see the fan controller chip on the bus, using
i2cdetect -y 10:
$ i2cdetect -y 10 0 1 2 3 4 5 6 7 8 9 a b c d e f 00: -- -- -- -- -- -- -- -- -- 0c -- -- -- 10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 2f 30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 50: -- 51 -- -- -- -- -- -- -- -- -- -- -- -- -- -- 60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 70: -- -- -- -- -- -- -- --
The fan is the
2f device. Test if you can turn off the fan using:
$ i2cset -y 10 0x2f 0x30 0x00
The fan should now be off. And to turn it back on:
$ i2cset -y 10 0x2f 0x30 0xff
To get the value of the fan setting, you can use:
$ i2cget -y 10 0x2f 0x30 0xff
What about setting the fan to a value between 0% (off) and 100% (full on) though? The value is hexadecimal, so
0xFF stands for
0. Using a high-ish number should be safe, right, to set the fan to a lower speed? Well, let's try it out.
First, set the fan speed to 'off':
$ i2cset -y 10 0x2f 0x30 0x00
Wait for the fan to spin down entirely, then set the fan to
0x64 in hex):
$ i2cset -y 10 0x2f 0x30 0x64
If you do this, you'll notice the fan comes back on, but hopefully at a much more pleasant speed. On one of my Noctua fans, 100/255 equates to about 40% speed, or 1200 rpm, and it's nearly silent.
Now that we can control the fan over I2C, we could write up a script to set fan speeds based on CPU temperatures manually, but there are a few other ways to control the fan speeds.
Note: The 'Fan' performance option inside
raspi-configcurrently has no effect on the operation of a fan through the EMC2301 chip.
Can I use Linux
Unfortunately, the standard way of controlling fan speeds based on sensor data on most common PC hardware doesn't seem to be supported on the CM4 IO Board's I2C chip.
If I install
fancontrol (following this guide), then I run
sudo sensors-detect, I get back the message:
Sorry, no sensors were detected. Either your system has no sensors, or they are not supported, or they are connected to an I2C or SMBus adapter that is not supported. If you find out what chips are on your board, check https://hwmon.wiki.kernel.org/device_support_status for driver status.
It did find 'clients' on the I2C bus at
0x2f, but it couldn't identify them. And when I ran
sudo pwmconfig, I got the message:
/usr/sbin/pwmconfig: There are no pwm-capable sensor modules installed
So it looks like that's out of the running, unfortunately. There's a really old patch for Linux to add the fan controller to the Linux source tree, but for some reason it never got worked on beyond early stages. There's also an issue discussing the IO Board fan controller in the Raspberry Pi Linux kernel project, in case you want to subscribe and see the latest updates.
GitHub user neg2led is maintaining an open source CM4 IO Board Fan controller driver, which is the next best thing. This driver is based on Traverse Technologies' EMC2301 hwmon driver.
To install it, I made sure I had I2C enabled as written above, and ran the following:
# Install the Raspberry Pi kernel headers, so the source build works. sudo apt install raspberrypi-kernel-headers # Install DKMS, to make updating the driver easier. sudo apt install dkms # Get the URL (tar.gz) of the latest release: https://github.com/neg2led/cm4io-fan/releases wget https://github.com/neg2led/cm4io-fan/archive/refs/tags/0.1.1.tar.gz # Expand the contents of the download into your /usr/src directory. sudo tar -xzvf 0.1.1.tar.gz -C /usr/src/ # Build/install the driver with DKMS. sudo dkms install cm4io-fan/0.1.1
At this point, the driver should be installed and will work after a reboot, once you configure it.
Note: I've only tested the driver on 64-bit Pi OS, but other users have reported it successfully compiles and works on 32-bit Pi OS as well.
Configuring the driver
Configuration can be done in the
/boot/config.txt file. Add a line like the following:
# Control fan speeds. dtoverlay=cm4io-fan,minrpm=1000,maxrpm=3000
This would set the fan to stay on at least at 1000 rpm at all times, and it would go up to 3000 rpm once the Pi's SoC reaches the
maxtemp, which by default is
5500 in millicelcius (55°C).
You can override other options such as temperature thresholds using the driver's config options.
You can also check the current fan speed with the following command:
$ cat /sys/class/hwmon/hwmon2/fan1_input 2146
The driver seems to work better on some CM4 boards than others, and may also have issues with certain PWM fans. One of the issues I encountered seems to be related to a potential bug in the reference design on the official IO Board.
If you encounter issues, check if the problem you hit is already documented in the cm4io-fan issue queue.
Basic Temperature-controlled fan script
As a final option you could write your own temperature-controlled fan script, which checks the current temperature, and boosts the fan speed accordingly. It's nowhere near as fully featured as the driver above, but it could work in a pinch:
There are a few different ways to interact with the EMC2301 fan controller on the Raspberry Pi Compute Module 4 IO Board (and a few other CM4 boards I've tested, like the Seaberry), but the cm4io-fan driver seems the most promising.
Hopefully you've learned a little about I2C in this post, too—I know I've learned a bit more about it, how PWM fans work, and even why tuning things like fan curves are important!