[Linux] Fan speed controller with custom speed curve

I was a bit annoyed by the noise of my FrameWork laptop’s fan, which was spinning way too fast for my taste, so I’ve written a little script to fix that over the weekend.

You can find it here: GitHub - TamtamHero/fw-fanctrl
There’s a bash script to install it as a service / uninstall it. I’ve only tested it on Ubuntu 20.04 but I think it should work on other distributions as well.

Feel free to improve it and push PRs, the script is very small (~100 lines of Python) and can be perfected/ made more customizable.

It’s based on the great work done by DHowett: GitHub - DHowett/fw-ectool


Question: I see that your default configuration has a minimum speed of “15” - does setting this value to “0” actually properly turn the fan completely off without causing other issues? (e.g. the fan not getting stuck in the “0” speed state when under load when the script is configured that the fan should turn back on at higher temps)

Also, with Ubuntu 22.04 (not a typo) imminent, do you think you could quickly test it with the beta?

And if you don’t already know about Ventoy, then I highly recommend using it to make your life easier when it comes to booting Linux ISOs:


I confirm that there is no issue using the “0” value for the fan, it goes to 0 and is able to go back to higher values without a problem.
You can use this command to try a particular speed %:
sudo ectool --interface=lpc fanduty <percentage>

And then you can check the actual fan speed in RPM with:
sudo ectool --interface=lpc pwmgetfanrpm

I’ll try on 22.04 later, didn’t know ventoy it looks nice, I usually use Etcher but it formats the key beforehand, which is sometimes annoying

Edit: tried on 22.04, and lm-sensors is not available to install via apt, no idea why
It’s possible to install it manually but I haven’t got the time to try it yet

Thank you @jean for this, and for pointing me in the direction of fw-ectool!

I have a small side question: on Arch, installing the fw-ectool-git package works fine, producing the ectool binary as expected. However, passing in --interface=lpc prevents things from working (as opposed to not specifying any value for --interface, or using --interface=fwk which doesn’t seem to be documented by works, added here):

$ sudo ectool --interface=lpc fanduty 0
Missing Chromium EC memory map.
Unable to establish host communication
Couldn't find EC

It’s just interesting that lpc works for you but not for me, given that we are both on a Framework laptop? Again, everything works fine for me if I avoid specifying lpc, I’m just curious why we see different behavior on the same(?) hardware. Perhaps we are using different version of ectool?

Indeed !
It still works for me when removing the interface parameter, my mistake.
I can’t remember why I came to the conclusion that this parameter was required, but it clearly isn’t :laughing:
I’ve updated the repo (along with a new feature, Strategies).
Thanks @dgquintas !

As of why does it work fine on ubuntu with --interface=lpc and not on Arch, I have no answer. Not very willing to dig but if someone knows, please let us know :nerd_face:

I made this account just to thank you for this. I was almost going to return my framework because the fans were wayy too loud and I couldn’t concentrate on my work because of them.


This is amazing work @jean ! Thanks for sharing. This thread should be pinned somewhere for visibility! I’d agree that the default fan curve seems overly aggressive.

My only suggestion for potential improvement would be a slow ramp up / down when a new target fan value is set. Would just be slightly nicer sound-wise. But I understand would make the code more complex when we can only set discrete values through ectool.

Do you have an idea of how frequently it’s possible to update the duty value using ectool? Could it be done multiple times per second for example? Not sure if this would have performance implications. This is interesting actually, I might have a play!

Thanks @BenVosper !

You can get a slower ramp up by increasing the MovingAverageInterval parameter in your config.
For ramp down however, the script takes the minimum temperature between average temp and current temp, so it’s an instant change in fan speed whenever the cpu load decrease suddenly.
To make the ramp down just as smooth as the ramp up, you can remove these 2 lines: fw-fanctrl/fanctrl.py at main · TamtamHero/fw-fanctrl · GitHub
and replace them with this:

currentTemp = self.getMovingAverageTemperature(self.movingAverageInterval)

If this new behavior works for you, feel free to make it configurable, PRs are welcomed :smiley:

About ectool, it’s a very lightweight program so there won’t be performance implications if you trigger it frequently, but I don’t think that the fan itself can mechanically react to more than 1 request/sec. Not 100% sure about it though, let me know if you fiddle with it and find something interesting !

1 Like