fnOS是一个不错的nas系统,使用方便简洁,但很多网友反馈该系统没有风扇控制的选项不能及时调整风扇转速,导致使用过程中风扇噪音过高影响使用体验,写了个风扇控制脚本,适用主板bios不具备风扇调速功能小主机使用。
风扇控制脚本:(fan_control.sh)
#!/bin/bash
# ================================== 配置项 ==================================
STATE_FILE="/var/run/fan_control.state"
LOG_FILE="/var/log/fan_control.log"
FAN_CTRL="/root/fanctrl"
CHECK_INTERVAL=60
MAX_CPU_TEMP=75 # 降低CPU阈值,更保守
MAX_HDD_TEMP=45 # 降低硬盘阈值,保护数据
SAFE_FAN_SPEED=25 # 降低默认转速,更安静
INCREASE_STEP=5 # 减小步进,更平滑
DECREASE_STEP=5 # 新增降温步进
MAX_FAN_SPEED=100
MIN_FAN_SPEED=20 # 新增最低转速
LO**AX_SIZE=10485760 # 日志最大10MB
# =============================================================================
# 初始化
init_files() {
if [ ! -f "$LOG_FILE" ]; then
touch "$LOG_FILE" 2>/dev/null || {
echo "错误:无法创建日志文件 $LOG_FILE"
exit 1
}
chmod 644 "$LOG_FILE"
fi
# 日志轮转检查
if [ -f "$LOG_FILE" ]; then
local log_size=$(stat -f%z "$LOG_FILE" 2>/dev/null || stat -c%s "$LOG_FILE" 2>/dev/null)
if [ "$log_size" -gt "$LO**AX_SIZE" ]; then
mv "$LOG_FILE" "${LOG_FILE}.old"
touch "$LOG_FILE"
log "日志文件已轮转(大小超过 ${LO**AX_SIZE} 字节)"
fi
fi
if [ ! -f "$STATE_FILE" ]; then
echo "current_speed=$SAFE_FAN_SPEED" > "$STATE_FILE"
echo "last_action=initialized" >> "$STATE_FILE"
echo "last_temp_check=$(date +%s)" >> "$STATE_FILE"
echo "consecutive_safe_checks=0" >> "$STATE_FILE"
if [ -x "$FAN_CTRL" ]; then
"$FAN_CTRL" "$SAFE_FAN_SPEED" >/dev/null 2>&1 && \
log "初始化风扇转速为 $SAFE_FAN_SPEED%" || \
log "警告:初始化风扇转速失败"
else
log "错误:未找到风扇控制程序 $FAN_CTRL"
exit 1
fi
fi
}
log() {
local timestamp=$(date +"%Y-%m-%d %H:%M:%S")
echo "[$timestamp] $1" >> "$LOG_FILE" 2>/dev/null
}
read_state() {
if [ -f "$STATE_FILE" ]; then
source "$STATE_FILE" 2>/dev/null || {
log "状态文件损坏,重新初始化"
rm -f "$STATE_FILE"
init_files
source "$STATE_FILE"
}
else
init_files
source "$STATE_FILE"
fi
}
update_state() {
local speed=$1
local action=$2
local safe_checks=${3:-0}
{
echo "current_speed=$speed"
echo "last_action=$action"
echo "last_temp_check=$(date +%s)"
echo "consecutive_safe_checks=$safe_checks"
} > "$STATE_FILE"
}
# 优化的CPU温度获取(多种方法,增加容错)
get_cpu_temp() {
local temp=""
# 方法1: thermal_zone(最常见)
if [ -z "$temp" ] && [ -f "/sys/class/thermal/thermal_zone0/temp" ]; then
local raw_temp=$(cat /sys/class/thermal/thermal_zone0/temp 2>/dev/null)
if [ -n "$raw_temp" ] && [ "$raw_temp" -gt 1000 ]; then
temp=$((raw_temp / 1000))
fi
fi
# 方法2: hwmon(适用于某些主板)
if [ -z "$temp" ]; then
local hwmon_temp=$(find /sys/class/hwmon -name "temp*_input" 2>/dev/null | head -1)
if [ -n "$hwmon_temp" ]; then
local raw_temp=$(cat "$hwmon_temp" 2>/dev/null)
if [ -n "$raw_temp" ] && [ "$raw_temp" -gt 1000 ]; then
temp=$((raw_temp / 1000))
fi
fi
fi
# 方法3: sensors命令(需要lm-sensors)
if [ -z "$temp" ] && command -v sensors >/dev/null 2>&1; then
temp=$(sensors 2>/dev/null | grep -E 'Core|Package|CPU' | grep -oP '\+\K[0-9]+' | sort -nr | head -1)
fi
# 方法4: 尝试ACPI
if [ -z "$temp" ] && command -v acpi >/dev/null 2>&1; then
temp=$(acpi -t 2>/dev/null | grep -oP '\d+\.\d+' | cut -d. -f1 | head -1)
fi
if [ -n "$temp" ] && [ "$temp" -gt 0 ] && [ "$temp" -lt 150 ]; then
echo "$temp"
return 0
fi
return 1
}
# 优化的硬盘温度获取
get_hdd_temp() {
local max_hdd_temp=0
local hdd_count=0
# 获取所有硬盘设备(排除回环设备和分区)
local hdd_devices=$(lsblk -d -o NAME,TYPE 2>/dev/null | awk '$2=="disk" && $1!~/loop/ {print $1}')
for dev in $hdd_devices; do
local device="/dev/$dev"
# 检查设备是否存在
[ -b "$device" ] || continue
# 检查是否休眠(避免唤醒)
local power_state=$(hdparm -C "$device" 2>/dev/null | grep -i 'drive state')
if echo "$power_state" | grep -qiE 'standby|sleeping'; then
continue
fi
local temp=""
# 优先使用smartctl
if command -v smartctl >/dev/null 2>&1; then
temp=$(smartctl -A "$device" 2>/dev/null | \
grep -iE '194|Temperature_Celsius' | \
awk '{print $10}' | head -1)
fi
# 备用hddtemp
if [ -z "$temp" ] && command -v hddtemp >/dev/null 2>&1; then
temp=$(hddtemp -n "$device" 2>/dev/null)
fi
# 验证温度有效性
if [ -n "$temp" ] && [ "$temp" -gt 0 ] && [ "$temp" -lt 100 ]; then
hdd_count=$((hdd_count + 1))
if [ "$temp" -gt "$max_hdd_temp" ]; then
max_hdd_temp=$temp
fi
fi
done
if [ "$max_hdd_temp" -gt 0 ]; then
echo "$max_hdd_temp"
return 0
fi
return 1
}
control_fan() {
local target_speed=$1
# 限制转速范围
if [ "$target_speed" -lt "$MIN_FAN_SPEED" ]; then
target_speed=$MIN_FAN_SPEED
elif [ "$target_speed" -gt "$MAX_FAN_SPEED" ]; then
target_speed=$MAX_FAN_SPEED
fi
if [ -x "$FAN_CTRL" ]; then
if "$FAN_CTRL" "$target_speed" >/dev/null 2>&1; then
return 0
else
log "错误:设置风扇转速 $target_speed% 失败"
return 1
fi
else
log "错误:风扇控制程序不可执行"
return 1
fi
}
# 优化的温度监控(更平滑的调速策略)
monitor_temps() {
read_state
local current_speed=${current_speed:-$SAFE_FAN_SPEED}
local safe_checks=${consecutive_safe_checks:-0}
local cpu_temp=$(get_cpu_temp)
local hdd_temp=$(get_hdd_temp)
# 至少需要一个有效温度
if [ -z "$cpu_temp" ] && [ -z "$hdd_temp" ]; then
log "警告:无法获取任何温度数据,保持当前转速"
return
fi
# 计算温度超标程度
local cpu_overheat=0
local hdd_overheat=0
if [ -n "$cpu_temp" ]; then
cpu_overheat=$((cpu_temp - MAX_CPU_TEMP))
[ $cpu_overheat -lt 0 ] && cpu_overheat=0
fi
if [ -n "$hdd_temp" ]; then
hdd_overheat=$((hdd_temp - MAX_HDD_TEMP))
[ $hdd_overheat -lt 0 ] && hdd_overheat=0
fi
local max_overheat=$cpu_overheat
[ $hdd_overheat -gt $max_overheat ] && max_overheat=$hdd_overheat
local target_speed=$current_speed
local action="温度正常"
if [ $max_overheat -gt 0 ]; then
# 超温:根据超标程度调整增幅
safe_checks=0
if [ $max_overheat -ge 10 ]; then
# 严重超温:快速增加
target_speed=$((current_speed + INCREASE_STEP * 3))
action="严重超温!快速提升转速"
elif [ $max_overheat -ge 5 ]; then
# 中度超温
target_speed=$((current_speed + INCREASE_STEP * 2))
action="中度超温,提升转速"
else
# 轻度超温
target_speed=$((current_speed + INCREASE_STEP))
action="轻度超温,微调转速"
fi
else
# 温度正常:逐步降低转速(避免频繁波动)
safe_checks=$((safe_checks + 1))
if [ $safe_checks -ge 3 ] && [ $current_speed -gt $SAFE_FAN_SPEED ]; then
# 连续3次检测正常才降速
target_speed=$((current_speed - DECREASE_STEP))
[ $target_speed -lt $SAFE_FAN_SPEED ] && target_speed=$SAFE_FAN_SPEED
action="温度持续正常,降低转速"
safe_checks=0
else
action="温度正常(${safe_checks}/3)"
fi
fi
# 执行调整
if [ $target_speed -ne $current_speed ]; then
if control_fan "$target_speed"; then
log "⚙️ $action | CPU: ${cpu_temp:-N/A}°C | HDD: ${hdd_temp:-N/A}°C | 转速: ${current_speed}% → ${target_speed}%"
update_state "$target_speed" "$action" "$safe_checks"
fi
else
log "✓ CPU: ${cpu_temp:-N/A}°C | HDD: ${hdd_temp:-N/A}°C | 转速: ${current_speed}% | ${action}"
fi
}
show_status() {
read_state
local cpu_temp=$(get_cpu_temp || echo "未检测到")
local hdd_temp=$(get_hdd_temp || echo "未检测到")
echo -e "${GREEN}===== 飞牛NAS风扇温控状态 =====${NC}"
echo -e "当前风扇转速: ${YELLOW}${current_speed}%${NC}"
echo -e "CPU温度: ${cpu_temp}°C (阈值: ${MAX_CPU_TEMP}°C)"
echo -e "硬盘温度: ${hdd_temp}°C (阈值: ${MAX_HDD_TEMP}°C)"
echo ""
echo "最后操作: $last_action"
echo "检测间隔: ${CHECK_INTERVAL}秒"
echo "转速范围: ${MIN_FAN_SPEED}%-${MAX_FAN_SPEED}%"
echo -e "${GREEN}==============================${NC}"
}
# 手动设置转速
set_speed() {
local speed=$1
if [ -z "$speed" ]; then
echo "用法: $0 --set-speed <20-100>"
exit 1
fi
if control_fan "$speed"; then
update_state "$speed" "手动设置为 ${speed}%" 0
echo -e "${GREEN}✓ 风扇转速已设置为 ${speed}%${NC}"
else
echo -e "${RED}✗ 设置失败${NC}"
exit 1
fi
}
main() {
if [ "$(id -u)" -ne 0 ]; then
echo -e "${RED}错误:请使用 sudo 运行${NC}"
exit 1
fi
# 检查fanctrl
if [ ! -x "$FAN_CTRL" ]; then
echo -e "${RED}错误:未找到 $FAN_CTRL 或无执行权限${NC}"
exit 1
fi
init_files
case "$1" in
--service)
log "========== 风扇温控服务启动 =========="
echo -e "${GREEN}风扇温控服务已启动(间隔: ${CHECK_INTERVAL}秒)${NC}"
echo "使用 Ctrl+C 停止,或运行: sudo $0 --status 查看状态"
while true; do
monitor_temps
sleep $CHECK_INTERVAL
done
;;
--status)
show_status
;;
--log)
if [ -f "$LOG_FILE" ]; then
tail -n 50 -f "$LOG_FILE"
else
echo "日志文件不存在"
fi
;;
--set-speed)
set_speed "$2"
;;
--restart)
log "手动重启服务"
control_fan "$SAFE_FAN_SPEED"
update_state "$SAFE_FAN_SPEED" "手动重启" 0
echo -e "${GREEN}✓ 服务已重启,转速重置为 ${SAFE_FAN_SPEED}%${NC}"
;;
--test)
echo "执行温度检测测试..."
local cpu=$(get_cpu_temp || echo "失败")
local hdd=$(get_hdd_temp || echo "失败")
echo "CPU温度: $cpu°C"
echo "硬盘温度: $hdd°C"
;;
*)
echo "飞牛NAS风扇温控脚本"
echo ""
echo "用法:"
echo " sudo $0 --service 启动温控服务"
echo " sudo $0 --status 查看当前状态"
echo " sudo $0 --log 查看实时日志"
echo " sudo $0 --set-speed
获取fanctrl控制程序
wget http://down.sgwbox.com/tools/x86/fanctrl /root/fanctrl
chmod +x /root/fanctrl
使用步骤
一、安装基础依赖(确保先执行):
apt update && apt install -y lm-sensors smartmontools hdparm hddtemp
初始化传感器
sudo sensors-detect --auto
这个命令会自动检测你的CPU温度传感器并加载驱动,全程自动无需手动操作。
二、验证依赖安装成功
1、测试CPU温度读取
sensors
2、测试硬盘温度读取
查看所有硬盘
lsblk -d -o NAME,SIZE,TYPE
测试读取第一块硬盘温度(替换sda为你的硬盘名)
sudo smartctl -A /dev/sda
3、测试fanctrl
测试设置风扇到30%
sudo /root/fanctrl 30
等待几秒,听声音是否变化
测试设置到50%
sudo /root/fanctrl 50
恢复到25%
sudo /root/fanctrl 25
三、创建脚本文件
sudo nano /usr/local/bin/fan_control.sh
赋予执行权限
sudo chmod +x /usr/local/bin/fan_control.sh
四、测试脚本
测试温度检测
sudo /usr/local/bin/fan_control.sh --test
执行温度检测测试...
CPU温度: 45°C
硬盘温度: 38°C
如果显示"失败":
- CPU温度失败:可以继续,脚本会尝试其他方法
- 硬盘温度失败:检查硬盘是否休眠,或者硬盘是否支持SMART
查看当前状态
sudo /usr/local/bin/fan_control.sh --status
===== 飞牛NAS风扇温控状态 =====
当前风扇转速: 25%
CPU温度: 45°C (阈值: 75°C)
硬盘温度: 38°C (阈值: 45°C)
最后操作: initialized
检测间隔: 60秒
转速范围: 20%-100%
==============================
前台测试运行(重要!)
sudo /usr/local/bin/fan_control.sh --service
观察5-10分钟:
- 每60秒会输出一条日志
- 检查温度读数是否正常
- 观察转速调整是否合理
- 听风扇声音是否有变化
如果一切正常,按 Ctrl+C 停止
五、配置开机自启
创建systemd服务
sudo nano /etc/systemd/system/fan-control.service
[Unit]
Description=Fan Control Service for FNOS NAS
After=multi-user.target[Service]
Type=simple
ExecStart=/usr/local/bin/fan_control.sh --service
Restart=always
RestartSec=10[Install]
WantedBy=multi-user.target
启用并启动服务
重载systemd配置
sudo systemctl daemon-reload
启用开机自启
sudo systemctl
立即启动服务
sudo systemctl start fan-control
检查服务状态
sudo systemctl status fan-control
● fan-control.service - Fan Control Service for FNOS NAS Loaded: loaded (/etc/systemd/system/fan-control.service; enabled) Active: active (running) since ...
看到 Active: active (running) 就表示成功了!