SeedStudio Recomputer RK3576 - Fan PWM control - prevent flutter

I followed the guide in:

I configured the fan-control.sh script as a systemd service.

I then compile openCV and noticed that the fan RPM is adjusted too frequently (“flutter” at threshold boundaries), this is not optimal because:

  1. It makes the fan noise much more noticable (RPM change every 5 seconds).
  2. If temp hovers around 40°C, PWM will bounce between 50 and 100 every 5s, which can shorten fan bearing life.

Can you contribute a kernel patch for this issue for Armbian and optimize the fan speed management?

thanks.

Armbian’s RK3588 device trees already support pwm-fan, thermal trip points, cooling levels, and hysteresis. A board-specific DTS patch could define multiple fan PWM levels. linux-rockchip/arch/arm64/boot/dts/rockchip/rk3588-hinlink-h88k.dts at rk-6.1-rkr5.1 · armbian/linux-rockchip · GitHub

1 Like

The “flutter” you’re seeing is the classic threshold-boundary oscillation of a polling user-space script: the shell reads the temperature every few seconds and writes a PWM value, but with a single trip point the fan flips every time the junction temperature crosses it. During an OpenCV build the big cores alternate between boost and idle, so the temperature naturally oscillates around 40 °C — and the fan follows every single crossing.

Two ways to fix it, depending on how much you want to touch the system:

1. Quick fix — add hysteresis to your fan-control.sh

Keep the systemd service, but make the transitions asymmetric so the fan only changes speed after the temperature has moved a margin past the threshold (plus a small debounce). Multi-level version:

bash

#!/bin/bash
# /usr/local/bin/fan-control.sh — multi-level + 5°C hysteresis
ZONE=/sys/class/thermal/thermal_zone1/temp   # big core (compile-heavy)
PWM=/sys/class/hwmon/hwmon6/pwm1
POLL=2

RISE=(55 65 75)     # °C where each level engages
PWMV=(64 128 200)   # 25% / 50% / 78% duty
HYST=5              # °C hysteresis on the way down

cur=0
while true; do
    t=$(($(cat "$ZONE") / 1000))
    if [ "$cur" -lt 2 ] && [ "$t" -ge "${RISE[$cur]}" ]; then
        cur=$((cur+1))
    elif [ "$cur" -gt 0 ] && [ "$t" -le "$(( ${RISE[$((cur-1))]} - HYST ))" ]; then
        cur=$((cur-1))
    fi
    echo "${PWMV[$cur]}" > "$PWM"
    sleep "$POLL"
done

With this, a temperature hovering at 40 °C keeps the fan at its current level until the SoC moves 5 °C away from the threshold — no more 50↔100 bouncing every 5 s. Pick the zone that reflects your load: thermal_zone1 (big core) for CPU builds; for mixed workloads you can use the max of zone0 (SoC) and zone1.

2. Proper fix — thermal-zones + pwm-fan in the DT

As loveforcircuits mentioned, Armbian’s RK3588 DTs already model this correctly: cooling-levels on the pwm-fan node plus a multi-trip cooling-map with hysteresis on the thermal zone. A minimal RK3576 equivalent:

dts

&pwm_fan {
    cooling-levels = <0 64 128 200 255>;
    #cooling-cells = <2>;
};

&thermal_zones {
    soc_thermal {
        trips {
            soc_alert1: soc-alert1 { temperature = <50000>; hysteresis = <5000>; type = "active"; };
            soc_alert2: soc-alert2 { temperature = <65000>; hysteresis = <5000>; type = "active"; };
            soc_alert3: soc-alert3 { temperature = <80000>; hysteresis = <3000>; type = "active"; };
        };
        cooling-maps {
            map1 { trip = <&soc_alert1>; cooling-device = <&pwm_fan 1 1>; };
            map2 { trip = <&soc_alert2>; cooling-device = <&pwm_fan 2 2>; };
            map3 { trip = <&soc_alert3>; cooling-device = <&pwm_fan 3 3>; };
        };
    };
};

The kernel thermal framework then steps the PWM levels with proper hysteresis instead of a binary on/off, with no userspace dependency — and the same behavior carries over to RK3588/RK3576 boards in the Armbian tree. Happy to help draft a full patch against the reComputer DT if you want to push it upstream.

One reliability note from the industrial side: this matters more than it looks. In 24/7 devices (digital signage, payment terminals, industrial HMI), a fan that re-accelerates every few seconds wears its bearings out much faster — industrial fans are typically rated for tens of thousands of hours at constant speed. Also keep in mind that 40 °C is a common ambient temperature inside enclosures, so tune the trip points against box temperature, not just the SoC reading.

Hope that helps!

1 Like

I want it to be 24/7 device, this is why it’s important to me.

I downloaded latest Armbian, there it looks like the fan behaves much better.

However, the fan doesn’t stop even when the CPU is quite cool (less than 40C).

I guess I configure this now that the support is builtin.

1 Like