在 NAS 上用 Linux 接管机箱风扇:基于硬盘温度的智能温控实战(ASRock B250M-HDV + nct6791)
我有一台自组 NAS,配置大致是:
- 主板:ASRock B250M-HDV
- 硬盘:目前 4 块 3.6TB 机械盘(后续计划扩展到 10 盘位)
- 系统:Linux(带
systemd,可用smartctl、sensors等工具)
关注点:
- NAS 的主要热源在 硬盘,CPU 负载较低且不易过热。
- 希望在 操作系统层面 精细控制机箱风扇(硬盘风道风扇),而不是每次进 BIOS 调整。
- 目标是:
- 风扇转速 跟随硬盘最高温度 自动变化;
- 保持 尽量安静 的前提下,确保硬盘温度在合理范围;
- 支持硬盘扩容(10 盘位),脚本不绑定具体盘数量;
- 有日志、有轮转,行为可观测,不是黑盒;
- 开机自启,挂了能自动重启;
- 不妨碍硬盘休眠(5 分钟无访问自动进入 standby)。
整体设计思路
设计原则
- 控制对象:机箱中负责硬盘风道的风扇(通过主板风扇控制芯片 nct6791 的
pwm4/pwm5控制)。 - 温度指标:
- 主指标:所有机械盘中的最高温度(SMART 属性 194 Temperature_Celsius)。
- 次指标:CPU 包温 只用于高温兜底。
- 控制策略:
- 每 6 分钟(360 秒) 读取一次硬盘温度(配合硬盘 5 分钟休眠,确保盘有机会睡着);
- 使用
smartctl -n standby -A:不唤醒已休眠的硬盘; - 根据最高盘温映射到几档 PWM 值(偏静音的分级策略);
- CPU 温度超阈值时强制提高风扇转速;
- 防止风扇频繁大幅度抖动,档位设计相对粗但稳定。
- 其他要求:
- 脚本输出 中文日志;
- 日志文件 单个最大 10MB,轮转时最多保留 10 个历史文件;
- 使用
systemd实现 开机自启 + 异常自动重启。
实战过程与关键步骤
确认主板与风扇控制芯片
查看主板信息:
sudo dmidecode -t baseboard输出示例(关键部分):
Manufacturer: ASRock
Product Name: B250M-HDV说明确认为 ASRock B250M-HDV 主板。
寻找硬件监控(hwmon)设备
先看系统中有哪些 hwmon 设备:
ls /sys/class/hwmon
for h in /sys/class/hwmon/hwmon*; do
echo "=== $h ==="
cat "$h/name" 2>/dev/null || true
ls "$h"
echo
done初始只看到类似:
hwmon0:nvme(NVMe 硬盘温度)hwmon1:coretemp(CPU 温度)
**没有任何 fanX_input 或 **pwmX,说明风扇控制芯片还没被驱动识别。
加载 nct6791 风扇控制芯片驱动
检查已加载模块:
lsmod | egrep "nct|it8|w836|hwmon" || echo "no related modules"若尚未加载,尝试:
modprobe nct6775 2>/dev/null || echo "nct6775 not available"
modprobe it87 2>/dev/null || echo "it87 not available"在这块主板上,实际加载成功的是 nct6791 软硬件组合,加载后再次查看:
ls /sys/class/hwmon
for h in /sys/class/hwmon/hwmon*; do
echo "=== $h ==="
cat "$h/name" 2>/dev/null || true
ls "$h"
echo
done这时多出一项:
=== /sys/class/hwmon/hwmon2 ===
nct6791
...
fan1_input fan2_input ... fan6_input
pwm1 pwm1_enable ...
pwm4 pwm4_enable ...
pwm5 pwm5_enable ...
...说明主板风扇/电压/温度的监控和控制接口已经暴露出来。
为了让驱动开机自动加载,可以写入 /etc/modules:
echo "nct6791" | sudo tee -a /etc/modules(确认不重复即可。)
验证哪一路 PWM 控制哪只风扇
进入对应 hwmon 目录(具体编号以实际为准,这里是 hwmon2):
cd /sys/class/hwmon/hwmon2查看当前 PWM 和风扇转速:
for x in pwm1 pwm2 pwm3 pwm4 pwm5 pwm6; do
printf "%s: " "$x"; cat "$x" 2>/dev/null || echo "(不存在)";
printf "%s_enable: " "$x"; cat "${x}_enable" 2>/dev/null || echo "(不存在)";
echo;
done
for f in fan1_input fan2_input fan3_input fan4_input fan5_input fan6_input; do
printf "%s: " "$f"; cat "$f" 2>/dev/null || echo "(不存在)";
done示例输出(关键段):
pwm4: 76
pwm4_enable: 5
pwm5: 76
pwm5_enable: 5
fan4_input: 675
fan5_input: 683此时 pwm4/pwm5 处于主板自动模式(enable=5),fan4/fan5 转速约 680 RPM。
实验验证:将 pwm4/pwm5 切换为手动并提高占空比(只往上调,避免停转):
cd /sys/class/hwmon/hwmon2
echo 1 > pwm4_enable
echo 1 > pwm5_enable
echo 160 > pwm4
echo 160 > pwm5
sleep 5
cat pwm4; cat pwm4_enable
cat pwm5; cat pwm5_enable
cat fan4_input; cat fan5_input结果类似:
pwm4: 160
pwm4_enable: 1
pwm5: 160
pwm5_enable: 1
fan4_input: 1215
fan5_input: 1215说明:
pwm4↔fan4_inputpwm5↔fan5_input- 且两路风扇就是硬盘风道对应的风扇(转速明显上升)
五、读取硬盘温度(SMART)
列出磁盘:
lsblk -o NAME,TYPE,SIZE,MODEL确认机械盘设备名为 /dev/sda、/dev/sdb、/dev/sdc、/dev/sdd 等。
使用 smartctl 读取 SMART 信息:
smartctl -A /dev/sda | egrep '^194|^190|Temperature'示例温度信息:
190 Airflow_Temperature_Cel ...
194 Temperature_Celsius ... 42 (0 17 0 0 0)整机硬盘温度概览命令:
for d in /dev/sd?; do
smartctl -A "$d" 2>/dev/null | awk "/^194 Temperature_Celsius/ {print \"$d:\", \$10\"°C\"}";
done我们定义 控制指标 为:所有 /dev/sd? 中温度最高的那一块盘。
六、硬盘休眠与监控频率的配合
为了让硬盘能正常休眠(用户设置为 5 分钟无访问自动进入 standby):
- 监控间隔必须大于休眠时间:设置
INTERVAL=360秒(6 分钟),确保硬盘有完整的 5 分钟空档可以进入休眠。 - **使用 **
smartctl -n standby -A:如果盘已经进入 standby,该命令不会唤醒它,只是拿不到温度(视为 0)。
这样既能根据硬盘温度调风扇,又不会妨碍硬盘休眠。
核心温控脚本:按硬盘最高温调节 pwm4/pwm5(带中文日志 + 日志轮转)
脚本路径建议:/usr/local/sbin/nas-hdd-fanctl.sh
内容如下:
#!/bin/bash
LOG_FILE="/var/log/nas-hdd-fanctl.log"
MAX_SIZE=$((10 * 1024 * 1024)) # 10MB
MAX_FILES=10
log_msg() {
# 日志滚动:大于 10MB 时,nas-hdd-fanctl.log -> .1 -> .2 ...,最多 10 个
if [ -f "$LOG_FILE" ]; then
size=$(stat -c%s "$LOG_FILE" 2>/dev/null || echo 0)
if [ "$size" -ge "$MAX_SIZE" ]; then
# 从高编号往低编号滚动
for ((i=MAX_FILES-1; i>=1; i--)); do
if [ -f "${LOG_FILE}.${i}" ]; then
if [ "$i" -eq "$MAX_FILES-1" ]; then
rm -f "${LOG_FILE}.${i}"
else
mv "${LOG_FILE}.${i}" "${LOG_FILE}.$((i+1))"
fi
fi
done
mv "$LOG_FILE" "${LOG_FILE}.1"
fi
fi
# 追加写中文日志:时间戳 + 文本
echo "$(date '+%Y-%m-%d %H:%M:%S') $*" >> "$LOG_FILE"
}
# 动态找到 nct6791 的 hwmon 目录
HWMON_DIR=""
for h in /sys/class/hwmon/hwmon*; do
if [ -f "$h/name" ] && grep -q "^nct6791$" "$h/name"; then
HWMON_DIR="$h"
break
fi
done
if [ -z "$HWMON_DIR" ]; then
log_msg "[错误] 未找到 nct6791 的 hwmon 设备,脚本退出"
sleep 60
exit 1
fi
log_msg "[信息] 使用 hwmon 目录: $HWMON_DIR"
PWM4="$HWMON_DIR/pwm4"
PWM5="$HWMON_DIR/pwm5"
PWM4_EN="$HWMON_DIR/pwm4_enable"
PWM5_EN="$HWMON_DIR/pwm5_enable"
# 确认 pwm4/pwm5 存在
if [ ! -f "$PWM4" ] || [ ! -f "$PWM5" ]; then
log_msg "[错误] 在 $HWMON_DIR 下未找到 pwm4/pwm5,脚本退出"
sleep 60
exit 1
fi
# 切换 pwm4/pwm5 到手动模式
[ -f "$PWM4_EN" ] && echo 1 > "$PWM4_EN"
[ -f "$PWM5_EN" ] && echo 1 > "$PWM5_EN"
log_msg "[信息] 已将 pwm4/pwm5 设置为手动模式"
# 初始较安静又不算太低的转速
SPEED=110
echo "$SPEED" > "$PWM4"
echo "$SPEED" > "$PWM5"
log_msg "[信息] 初始风扇转速设为 $SPEED"
# 读取所有 /dev/sd? 的最高硬盘温度(SMART 194 Temperature_Celsius)
# 使用 -n standby:如果磁盘已休眠则不唤醒,返回空温度
get_max_hdd_temp() {
local max=0
local found=0
for d in /dev/sd?; do
[ -b "$d" ] || continue
t=$(smartctl -n standby -A "$d" 2>/dev/null | awk '/^194 Temperature_Celsius/ {print $10; exit}')
if echo "$t" | grep -q '^[0-9][0-9]*$'; then
found=1
[ "$t" -gt "$max" ] && max="$t"
fi
done
if [ "$found" -eq 0 ]; then
echo 0
else
echo "$max"
fi
}
# 获取 CPU 包温(Package id 0),整数 °C;失败则输出空
get_cpu_temp() {
sensors 2>/dev/null | awk '
/Package id 0:/ {
gsub(/[^0-9.]/,"",$4);
printf "%d", $4;
exit
}'
}
# 检测间隔(秒)——与硬盘休眠 5 分钟配合使用:这里用 360 秒(6 分钟)
INTERVAL=360
log_msg "[信息] 开始温控循环,检测间隔 ${INTERVAL} 秒(使用 -n standby 保护硬盘休眠)"
while true; do
max_hdd=$(get_max_hdd_temp)
cpu_t=$(get_cpu_temp)
# 以"硬盘最高温"为主的风扇档位策略(偏静音)
if [ "$max_hdd" -lt 32 ]; then
SPEED=90 # 很凉,尽量安静
elif [ "$max_hdd" -lt 37 ]; then
SPEED=110 # 正常温度,轻微风
elif [ "$max_hdd" -lt 42 ]; then
SPEED=150 # 稍热,适度提高
else
SPEED=200 # 偏热,显著加强风量
fi
# CPU 高温兜底:CPU >= 70°C 强制提速
if echo "$cpu_t" | grep -q '^[0-9][0-9]*$' && [ "$cpu_t" -ge 70 ]; then
SPEED=220
fi
# 保险:限制在 [80,230]
if [ "$SPEED" -lt 80 ]; then
SPEED=80
elif [ "$SPEED" -gt 230 ]; then
SPEED=230
fi
# 应用到硬盘风道两个风扇
echo "$SPEED" > "$PWM4"
echo "$SPEED" > "$PWM5"
log_msg "[循环] 硬盘最高温=${max_hdd}°C CPU温度=${cpu_t:-NA}°C 设定转速=${SPEED}"
sleep "$INTERVAL"
done注意事项:
- 控制对象仅为
pwm4/pwm5对应的风扇(硬盘风道),CPU 风扇依然由主板 BIOS / SmartFan 负责; /dev/sd?会动态适配将来扩展到 10 盘位甚至更多;- 日志文件路径为
/var/log/nas-hdd-fanctl.log,单个文件 10MB 自动轮转,最多保留 10 个历史文件。
使用 systemd 配置开机自启
1. 赋予脚本执行权限
chmod +x /usr/local/sbin/nas-hdd-fanctl.sh2. 创建 systemd 服务单元
文件路径:/etc/systemd/system/nas-hdd-fan.service
内容:
[Unit]
Description=NAS HDD temperature based fan control (pwm4/pwm5)
After=multi-user.target
Wants=multi-user.target
[Service]
Type=simple
ExecStart=/usr/local/sbin/nas-hdd-fanctl.sh
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target3. 重新加载并启用服务
systemctl daemon-reload
systemctl enable --now nas-hdd-fan.service查看状态:
systemctl status nas-hdd-fan.service查看日志:
tail -f /var/log/nas-hdd-fanctl.log实际效果与可调参数
关注指标
- 硬盘温度:通过 SMART 194 属性查看长期稳定区间(建议长期控制在 40–45°C 内)。
- 风扇转速:通过
sensors查看fan4、fan5的 RPM。 - 硬盘休眠状态:通过
hdparm -C /dev/sdX查看盘是否处于 standby。
可调参数
- 检测间隔:
INTERVAL=360(可按需求改为 300/600 秒等,注意要大于硬盘休眠时间)。 - 档位温度阈值:
32/37/42°C可根据实际盘温和噪音感受微调。 - PWM 档位:
90/110/150/200/220可根据风扇特性和噪音容忍度微调。 - 日志轮转大小:
MAX_SIZE=$((10 * 1024 * 1024))(单文件 10MB,可按需调整)。
辅助监控脚本
为了方便实时观察温度和转速,可以使用 /root/check-nas-therm.sh(持续刷新版):
bash /root/check-nas-therm.sh默认每 10 秒刷新一次,也可以自定义间隔:
INTERVAL=5 bash /root/check-nas-therm.sh总结
这套方案主要解决了以下几个问题:
- 利用 nct6791 芯片,在 Linux 系统层面直接接管风扇控制,不再依赖 BIOS,支持脚本化/自动化。
- 以 硬盘最高温度 为核心指标,结合 CPU 高温兜底,实现更符合 NAS 场景的温控策略。
- 通过 中文日志 + 日志轮转(单文件 10MB,最多 10 个)保证脚本行为可观测、可审计,便于日后优化与排障。
- 使用 systemd 实现 开机自启 + 异常自动重启,真正做到”设置一次,长期托管”。
- 使用
smartctl -n standby -A** + 合理的监控间隔(6 分钟)**,确保硬盘能正常进入 5 分钟休眠,不会被监控脚本频繁唤醒。
如果你也用类似的主板/NAS 方案,可以在此基础上根据自己的硬盘温度/噪音偏好/休眠需求,微调几个关键参数,就能获得一套非常贴合自己使用习惯的自动温控系统。