Fix fan operation on ODROID HC4


    The ODROID HC4 has a PWM fan built into its transparent case. The Armbian download page suggests to configure fancontrol for fan support – but this only works with Armbian kernels up to around 6.0. With later kernels it seemingly never spins up – or it does, but quicky spins down. With two HDDs plugged into the slots at the top the idle temparature can get up to 60+°C that way. This guide fixes it.

    Who is it for?

    Owners of an ODROID HC4 SoC running the current stable distribution (Armbian Jelly or Bookworm at the time of writing this) and at least one 3.5" HDD generating enough of an inner temperature to warrant concern.


    • lm_sensors – ships fancontrol as one of the ways to set PWM fan speed

    The whole reason for this behavior is the thermal driver being able to take control of the fan speed. So when fancontrol is run and the fan speed is set according to its config and the current temperature reading, thermal simply overrides the speed according to the trip point config of the thermal zone. In essence, fancontrol and the thermal driver are mutually exclusive. A decision must be made: Which one should take care of the fan?

    The fancontrol way

    As written on the Armbian page, create a /etc/fancontrol file. Edit INTERVAL, MINTEMP, MAXTEMP, MINPWM and MAXPWM if you wish to change interval, the trip point and max speed of the fan.

    DEVPATH=hwmon0=devices/virtual/thermal/thermal_zone0 hwmon2=devices/platform/pwm-fan
    DEVNAME=hwmon0=cpu_thermal hwmon2=pwmfan

    Start and enable the fancontrol service.

    systemctl enable --now fancontrol.service

    Configure thermal to leave fan control to user space.

    echo disabled > /sys/devices/virtual/thermal/thermal_zone0/mode

    To persist this across reboots, a udev rule can be written to /etc/udev/rules.d/99-disable-thermal-module.rules.

    SUBSYSTEM=="thermal", ATTR{type}=="cpu-thermal", ATTR{mode}="disabled"

    The kernel way

    Simply configure the trip points for thermal_zone0. The defaults are 60°C (active, trip_point_3_temp), 85°C (passive, trip_point_0_temp), 95°C (hot, trip_point_1_temp) and 110°C (critical, trip_point_2_temp). Note that the values are in millidegrees Celsius.

    echo 50000 > /sys/devices/virtual/thermal/thermal_zone0/trip_point_0_temp # "passive" trip point
    echo 60000 > /sys/devices/virtual/thermal/thermal_zone0/trip_point_3_temp # "active" trip point
    echo 70000 > /sys/devices/virtual/thermal/thermal_zone0/trip_point_1_temp # "hot" trip point
    echo 90000 > /sys/devices/virtual/thermal/thermal_zone0/trip_point_2_temp # "critical" trip point

    To persist them across reboots, once again a udev rule can be used.

    # passive trip point
    SUBSYSTEM=="thermal", ATTR{type}=="cpu-thermal", ATTR{trip_point_?_type}=="passive" ATTR{trip_point_?_temp}="50000"
    # active trip point
    SUBSYSTEM=="thermal", ATTR{type}=="cpu-thermal", ATTR{trip_point_?_type}=="active" ATTR{trip_point_?_temp}="60000"
    # hot trip point
    SUBSYSTEM=="thermal", ATTR{type}=="cpu-thermal", ATTR{trip_point_?_type}=="hot" ATTR{trip_point_?_temp}="70000"
    # critical trip point
    SUBSYSTEM=="thermal", ATTR{type}=="cpu-thermal", ATTR{trip_point_?_type}=="critical" ATTR{trip_point_?_temp}="90000"

    The caveat

    I've found this method a bit difficult to configure: It spins up the fan to 100% on reaching any trip point other than passive. The latter ironically does not do passive cooling as the name suggests – the fan stays off upon reaching it. I've yet to find a way how to set fan speed based on the various trip points using the thermal driver, so for now I recommend the fancontrol way.