收起左侧

~fnOS通过fanctr实现风扇转速控制降噪~

2
回复
281
查看
[ 复制链接 ]

5

主题

28

回帖

0

牛值

江湖小虾

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) 就表示成功了!

收藏
送赞 1
分享

0

主题

6

回帖

0

牛值

江湖小虾

支持一下

2

主题

9

回帖

0

牛值

江湖小虾

不是有一个coolcontrol的容器可以控制么?自从官方提供了IT87的驱动后,我的板载传感器都可以识别和控制了

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则