收起左侧

拾光坞G2刷飞牛后智能风扇脚本

17
回复
1305
查看
[ 复制链接 ]

5

主题

20

回帖

0

牛值

江湖小虾

2025-12-18 21:27:37 显示全部楼层 阅读模式
  1. 新增 get_cpu_temp() 函数:从 /sys/class/thermal/thermal_zone*/temp 读取 CPU 温度
  2. 主逻辑取 max(硬盘最高温, CPU温度)
  3. 兼容无 lm-sensors 的精简系统(不依赖 sensors 命令)
  4. 保留原有 SATA/NVMe 支持
  • ✅ 修复 SATA 硬盘温度读取(适配你的 194 Temperature_Celsius 输出)
  • ✅ 支持 NVMe 和 SATA 混合环境
  • 不依赖 jqlsblk,兼容飞牛精简系统
  • ✅ 自动识别 /dev/sdX/dev/nvmeXn1
  • ✅ 温控曲线可自定义
  • ✅ 日志记录 + 开机自启友好

完整脚本:/root/fan_auto.sh(含 CPU + 硬盘双温控)

bash
编辑
#!/bin/bash

# 拾光坞 G2 风扇自动调速脚本(CPU + 硬盘双温控版)
# 支持 SATA / NVMe / CPU 温度 | 无需 jq / sensors | 适配飞牛 fnOS
# 作者:Qwen | 2025-12-18

FANCTRL="/root/fanctrl"
LOGFILE="/var/log/fan_auto.log"
INTERVAL=30

# 温度 → 风扇转速映射(百分比)
declare -A SPEED_MAP=(
    [0]=30   # <=40°C
    [41]=35
    [45]=40
    [50]=50
    [55]=60
    [60]=70
    [65]=80
    [70]=90
    [75]=100
)

log() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" | tee -a "$LOGFILE"
}

# 获取所有硬盘设备
get_disks() {
    for dev in /dev/sd[a-z]; do [[ -e "$dev" ]] && echo "$dev"; done
    for dev in /dev/nvme[0-9]n[0-9]; do [[ -e "$dev" ]] && echo "$dev"; done
}

# 获取硬盘温度
get_disk_temp() {
    local dev="$1"
    if [[ -z "$dev" ]] || [[ ! -e "$dev" ]]; then
        echo 0
        return
    fi

    local temp=""
    temp=$(smartctl -A "$dev" 2>/dev/null | awk '$1 == "194" {print $10; exit}')
    if [[ -z "$temp" ]] || ! [[ "$temp" =~ ^[0-9]+$ ]]; then
        temp=$(smartctl -A "$dev" 2>/dev/null | awk '/^Temperature:/ && /Celsius/ {print $2; exit}')
    fi

    if [[ "$temp" =~ ^[0-9]+$ ]] && [[ $temp -ge 0 ]] && [[ $temp -le 80 ]]; then
        echo "$temp"
    else
        echo 0
    fi
}

# 获取 CPU 温度(单位:°C)
get_cpu_temp() {
    local max_temp=0
    local temp_file

    # 方法1:通过 /sys/class/thermal/(推荐,无需额外工具)
    for zone in /sys/class/thermal/thermal_zone*/; do
        if [[ -f "${zone}type" ]] && [[ -f "${zone}temp" ]]; then
            type=$(cat "${zone}type" 2>/dev/null)
            temp_val=$(cat "${zone}temp" 2>/dev/null)
            # 转换为 °C(值通常是 1000 倍)
            temp_c=$((temp_val / 1000))
            # 只采集 CPU 相关的 zone(如 x86_pkg_temp, coretemp, cpu-*)
            if [[ "$type" == *"cpu"* || "$type" == *"core"* || "$type" == *"x86"* ]]; then
                if [[ $temp_c -gt $max_temp ]]; then
                    max_temp=$temp_c
                fi
            fi
        fi
    done

    # 如果没读到,尝试备用方法(某些系统用 hwmon)
    if [[ $max_temp -eq 0 ]]; then
        for hwmon in /sys/class/hwmon/hwmon*/; do
            if [[ -f "${hwmon}name" ]]; then
                name=$(cat "${hwmon}name" 2>/dev/null)
                if [[ "$name" == "coretemp" || "$name" == "k10temp" || "$name" == "zenpower" ]]; then
                    for temp_input in "${hwmon}"temp*_input; do
                        if [[ -f "$temp_input" ]]; then
                            temp_c=$(( $(cat "$temp_input") / 1000 ))
                            if [[ $temp_c -gt $max_temp ]]; then
                                max_temp=$temp_c
                            fi
                        fi
                    done
                fi
            fi
        done
    fi

    # 限制合理范围
    if [[ $max_temp -ge 0 ]] && [[ $max_temp -le 100 ]]; then
        echo $max_temp
    else
        echo 0
    fi
}

# 根据温度获取风扇转速
get_speed_by_temp() {
    local temp=$1
    local speed=30
    for t in 75 70 65 60 55 50 45 41 0; do
        if [[ $temp -ge $t ]]; then
            speed=${SPEED_MAP[$t]}
            break
        fi
    done
    echo $speed
}

# 主程序
main() {
    log "=== 风扇自动调速脚本启动(CPU + 硬盘双温控)==="
    log "检测间隔: ${INTERVAL} 秒"

    while true; do
        # 1. 获取硬盘最高温度
        disk_max=0
        disks=($(get_disks))
        for disk in "${disks[@]}"; do
            temp=$(get_disk_temp "$disk")
            [[ $temp -gt $disk_max ]] && disk_max=$temp
            log "DEBUG: Disk $disk = ${temp}°C"
        done

        # 2. 获取 CPU 温度
        cpu_temp=$(get_cpu_temp)
        log "DEBUG: CPU = ${cpu_temp}°C"

        # 3. 取两者最大值
        max_temp=$(( disk_max > cpu_temp ? disk_max : cpu_temp ))

        # 4. 控制风扇
        target_speed=$(get_speed_by_temp $max_temp)
        current_speed=$(cat /tmp/fan_current_speed 2>/dev/null || echo "-1")

        if [[ "$current_speed" != "$target_speed" ]]; then
            if [[ -x "$FANCTRL" ]]; then
                "$FANCTRL" "$target_speed" >/dev/null 2>&1
                echo "$target_speed" > /tmp/fan_current_speed
                log "🌡️ 最高温度: ${max_temp}°C (CPU:${cpu_temp}°C, Disk:${disk_max}°C) → 🌀 风扇: ${target_speed}%"
            else
                log "❌ fanctrl 工具缺失!"
                exit 1
            fi
        else
            log "📊 当前最高: ${max_temp}°C → 风扇保持 ${target_speed}%"
        fi

        sleep $INTERVAL
    done
}

main

使用步骤(一次性设置)

bash
编辑
# 1. 保存脚本
nano /root/fan_auto.sh
# ← 粘贴上面全部内容,Ctrl+O 回车保存,Ctrl+X 退出

# 2. 赋予执行权限
chmod +x /root/fan_auto.sh

# 3. 确保已安装 smartmontools
apt update && apt install -y smartmontools

# 4. 确保 fanctrl 已部署
wget -O /root/fanctrl http://down.sgwbox.com/tools/x86/fanctrl
chmod +x /root/fanctrl

# 5. 测试运行(观察日志)
nohup /root/fan_auto.sh > /dev/null 2>&1 &
tail -f /var/log/fan_auto.log

🔄 设置开机自启(持久化)

bash
编辑
# 挂载根分区为可写(飞牛默认只读)
mount -o remount,rw /

# 添加到 rc.local
cat >> /etc/rc.local << 'EOF'

# 启动风扇自动控制
if [ -x /root/fan_auto.sh ]; then
    nohup /root/fan_auto.sh > /dev/null 2>&1 &
fi
EOF

# 确保 rc.local 可执行
chmod +x /etc/rc.local

最终效果:

image.png

收藏
送赞 1
分享

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x

5

主题

20

回帖

0

牛值

江湖小虾

2025-12-18 22:08:38 楼主 显示全部楼层

以下是 最终整合版风扇自动调速脚本,专为 拾光坞 G2 + 飞牛系统(fnOS) 优化,包含:

✅ 同时监控 CPU 温度 + SATA/NVMe 硬盘温度

✅ DEBUG 开关:默认关闭调试日志,避免刷屏

✅ 日志自动限行:最多保留 300 行(约 1~2 天),防止磁盘写满

✅ 不依赖 jq / sensors / logrotate,兼容飞牛精简环境

✅ 支持开机自启、异常退出保护

📜 文件名:/root/fan_auto.sh

#!/bin/bash

# 拾光坞 G2 风扇自动调速脚本(最终整合版)
# 功能:CPU + 硬盘双温控 | 日志限行 | DEBUG开关 | 飞牛 fnOS 专用
# 作者:Qwen | 2025-12-18

# ====== 配置区(可按需修改)======
FANCTRL="/root/fanctrl"
LOGFILE="/var/log/fan_auto.log"
INTERVAL=30                     # 检测间隔(秒)
MAX_LOG_LINES=300               # 日志最多保留行数
DEBUG_LOG=false                 # true=开启DEBUG日志,false=仅关键事件

# 温度 → 风扇转速映射(百分比)
declare -A SPEED_MAP=(
    [0]=30   # <=40°C
    [41]=35
    [45]=40
    [50]=50
    [55]=60
    [60]=70
    [65]=80
    [70]=90
    [75]=100
)
# ==============================

# 安全创建日志目录
mkdir -p "$(dirname "$LOGFILE")"

# 带自动截断的日志函数
log() {
    local msg="[$(date '+%Y-%m-%d %H:%M:%S')] $*"
    echo "$msg" >> "$LOGFILE"
    # 自动限制日志行数
    if [[ -f "$LOGFILE" ]]; then
        tail -n "$MAX_LOG_LINES" "$LOGFILE" > "$LOGFILE.tmp" && mv "$LOGFILE.tmp" "$LOGFILE"
    fi
}

debug_log() {
    if [[ "$DEBUG_LOG" == "true" ]]; then
        log "DEBUG: $*"
    fi
}

# 获取硬盘设备列表
get_disks() {
    for dev in /dev/sd[a-z]; do [[ -e "$dev" ]] && echo "$dev"; done
    for dev in /dev/nvme[0-9]n[0-9]; do [[ -e "$dev" ]] && echo "$dev"; done
}

# 获取单个硬盘温度
get_disk_temp() {
    local dev="$1"
    [[ -z "$dev" ]] || [[ ! -e "$dev" ]] && { echo 0; return; }

    local temp=""
    temp=$(smartctl -A "$dev" 2>/dev/null | awk '$1 == "194" {print $10; exit}')
    if [[ -z "$temp" ]] || ! [[ "$temp" =~ ^[0-9]+$ ]]; then
        temp=$(smartctl -A "$dev" 2>/dev/null | awk '/^Temperature:/ && /Celsius/ {print $2; exit}')
    fi

    if [[ "$temp" =~ ^[0-9]+$ ]] && [[ $temp -ge 0 ]] && [[ $temp -le 80 ]]; then
        echo "$temp"
    else
        echo 0
    fi
}

# 获取 CPU 温度(通过 sysfs)
get_cpu_temp() {
    local max_temp=0
    for zone in /sys/class/thermal/thermal_zone*/; do
        [[ -f "${zone}type" ]] && [[ -f "${zone}temp" ]] || continue
        type=$(cat "${zone}type" 2>/dev/null)
        temp_val=$(cat "${zone}temp" 2>/dev/null)
        temp_c=$((temp_val / 1000))
        if [[ "$type" == *"cpu"* || "$type" == *"core"* || "$type" == *"x86"* ]]; then
            (( temp_c > max_temp )) && max_temp=$temp_c
        fi
    done

    # 备用:hwmon
    if [[ $max_temp -eq 0 ]]; then
        for hwmon in /sys/class/hwmon/hwmon*/; do
            [[ -f "${hwmon}name" ]] || continue
            name=$(cat "${hwmon}name" 2>/dev/null)
            if [[ "$name" == "coretemp" || "$name" == "k10temp" ]]; then
                for input in "${hwmon}"temp*_input; do
                    [[ -f "$input" ]] || continue
                    temp_c=$(( $(cat "$input") / 1000 ))
                    (( temp_c > max_temp )) && max_temp=$temp_c
                done
            fi
        done
    fi

    (( max_temp >= 0 && max_temp <= 100 )) && echo $max_temp || echo 0
}

# 根据温度返回风扇转速
get_speed_by_temp() {
    local temp=$1 speed=30
    for t in 75 70 65 60 55 50 45 41 0; do
        if (( temp >= t )); then
            speed=${SPEED_MAP[$t]}
            break
        fi
    done
    echo $speed
}

# 主程序
main() {
    log "=== 风扇自动调速脚本启动(CPU+硬盘双温控)==="
    log "配置: INTERVAL=${INTERVAL}s, MAX_LOG_LINES=${MAX_LOG_LINES}, DEBUG=${DEBUG_LOG}"

    while true; do
        # 1. 硬盘最高温
        disk_max=0
        disks=($(get_disks))
        for disk in "${disks[@]}"; do
            temp=$(get_disk_temp "$disk")
            (( temp > disk_max )) && disk_max=$temp
            debug_log "Disk $disk = ${temp}°C"
        done

        # 2. CPU 温度
        cpu_temp=$(get_cpu_temp)
        debug_log "CPU = ${cpu_temp}°C"

        # 3. 取最大值
        max_temp=$(( disk_max > cpu_temp ? disk_max : cpu_temp ))

        # 4. 控制风扇
        target_speed=$(get_speed_by_temp $max_temp)
        current_speed=$(cat /tmp/fan_current_speed 2>/dev/null || echo "-1")

        if [[ "$current_speed" != "$target_speed" ]]; then
            if [[ -x "$FANCTRL" ]]; then
                "$FANCTRL" "$target_speed" >/dev/null 2>&1
                echo "$target_speed" > /tmp/fan_current_speed
                log "🌡️ 最高温度: ${max_temp}°C (CPU:${cpu_temp}°C, Disk:${disk_max}°C) → 🌀 风扇: ${target_speed}%"
            else
                log "❌ 错误:fanctrl 工具缺失或不可执行!"
                exit 1
            fi
        else
            debug_log "当前最高: ${max_temp}°C → 风扇保持 ${target_speed}%"
        fi

        sleep $INTERVAL
    done
}

# 启动
main

0

主题

11

回帖

0

牛值

江湖小虾

2025-12-19 07:26:11 显示全部楼层

这个性价比高

5

主题

20

回帖

0

牛值

江湖小虾

2025-12-19 11:23:43 楼主 显示全部楼层

最终优化版风扇自动调速脚本 v3.1,整合了你提出的核心需求:

CPU 与硬盘分开控温策略
硬盘休眠时不读取 SMART(避免唤醒)
智能融合转速:抑制 CPU 瞬时高温导致的风扇狂转(当硬盘较凉时)
日志限行 + DEBUG 开关 + 飞牛系统兼容


📜 文件名:/root/fan_auto.sh

#!/bin/bash

# 拾光坞 G2 风扇智能调速脚本 v3.1
# ✅ 分离 CPU / 硬盘控制策略
# ✅ 硬盘休眠时不读取 SMART(保护休眠)
# ✅ 智能融合:硬盘凉时抑制 CPU 引起的风扇噪音
# ✅ 日志自动限行 | 无 jq / sensors 依赖
# 作者:Qwen | 2025-12-19

# ====== 配置区(可按需调整)======
FANCTRL="/root/fanctrl"
LOGFILE="/var/log/fan_auto.log"
MAX_LOG_LINES=300
DEBUG_LOG=false

# CPU 控制(响应快)
CPU_INTERVAL=15
CPU_SPEED_MAP=(
    [0]=30   # <=40°C
    [41]=35
    [45]=40
    [50]=50
    [55]=60
    [60]=70
    [65]=80
    [70]=90
    [75]=100
)

# 硬盘控制(保守,仅活跃盘参与)
DISK_INTERVAL=60
DISK_IDLE_THRESHOLD=60  # 60秒无I/O视为休眠
DISK_SPEED_MAP=(
    [0]=30
    [41]=35
    [45]=40
    [50]=50
    [55]=60
    [60]=70
    [65]=80
    [70]=90
    [75]=100
)

# 智能融合参数
DISK_COOL_THRESHOLD=45  # 硬盘 ≤ 此温度视为“凉快”
CPU_NOISE_LIMIT=60      # 硬盘凉快时,CPU策略转速上限(%)
# ================================

mkdir -p "$(dirname "$LOGFILE")"

log() {
    local msg="[$(date '+%Y-%m-%d %H:%M:%S')] $*"
    echo "$msg" >> "$LOGFILE"
    tail -n "$MAX_LOG_LINES" "$LOGFILE" > "$LOGFILE.tmp" 2>/dev/null && mv "$LOGFILE.tmp" "$LOGFILE"
}
debug_log() { [[ "$DEBUG_LOG" == "true" ]] && log "DEBUG: $*"; }

# 获取 CPU 温度
get_cpu_temp() {
    local max_temp=0
    for zone in /sys/class/thermal/thermal_zone*/; do
        [[ -f "${zone}type" ]] && [[ -f "${zone}temp" ]] || continue
        type=$(cat "${zone}type" 2>/dev/null)
        temp_c=$(( $(cat "${zone}temp" 2>/dev/null) / 1000 ))
        if [[ "$type" == *"cpu"* || "$type" == *"core"* || "$type" == *"x86"* ]]; then
            (( temp_c > max_temp )) && max_temp=$temp_c
        fi
    done
    (( max_temp >= 0 && max_temp <= 100 )) && echo $max_temp || echo 0
}

# 判断硬盘是否活跃(基于 /proc/diskstats I/O)
is_disk_active() {
    local dev="$1"
    local base_dev=$(basename "$dev")
    local stats_file="/proc/diskstats"

    if ! grep -q "$base_dev" "$stats_file" 2>/dev/null; then
        return 0  # 无法判断,默认视为活跃(安全)
    fi

    local current_reads current_writes
    read _ _ _ current_reads _ _ _ current_writes _ < <(awk -v dev="$base_dev" '$3 == dev {print $0}' "$stats_file")

    local last_file="/tmp/fan_disk_${base_dev}.io"
    if [[ -f "$last_file" ]]; then
        read last_reads last_writes last_time < "$last_file"
        local now=$(date +%s)

        if [[ $current_reads != "$last_reads" ]] || [[ $current_writes != "$last_writes" ]] || (( now - last_time > DISK_IDLE_THRESHOLD )); then
            echo "$current_reads $current_writes $now" > "$last_file"
            return 0  # 活跃
        else
            return 1  # 休眠
        fi
    else
        echo "$current_reads $current_writes $(date +%s)" > "$last_file"
        return 0
    fi
}

# 获取活跃硬盘最高温度(跳过休眠盘)
get_active_disk_max_temp() {
    local max_temp=0
    for dev in /dev/sd[a-z] /dev/nvme[0-9]n[0-9]; do
        [[ -e "$dev" ]] || continue
        if is_disk_active "$dev"; then
            debug_log "Disk $dev is ACTIVE"
            local temp=$(smartctl -A "$dev" 2>/dev/null | awk '$1 == "194" {print $10; exit}')
            if [[ -z "$temp" ]] || ! [[ "$temp" =~ ^[0-9]+$ ]]; then
                temp=$(smartctl -A "$dev" 2>/dev/null | awk '/^Temperature:/ && /Celsius/ {print $2; exit}')
            fi
            if [[ "$temp" =~ ^[0-9]+$ ]] && (( temp >= 0 && temp <= 80 )); then
                (( temp > max_temp )) && max_temp=$temp
                debug_log "Disk $dev temp = ${temp}°C"
            fi
        else
            debug_log "Disk $dev is IDLE (skip SMART read)"
        fi
    done
    echo $max_temp
}

# 根据温度和映射表获取转速
get_speed() {
    local temp=$1
    declare -n map=$2
    local speed=30
    for t in 75 70 65 60 55 50 45 41 0; do
        if (( temp >= t )); then
            speed=${map[$t]}
            break
        fi
    done
    echo $speed
}

# 主循环
main() {
    log "=== 风扇智能调速 v3.1 启动 ==="
    log "策略: CPU(${CPU_INTERVAL}s) + Disk(${DISK_INTERVAL}s, idle>${DISK_IDLE_THRESHOLD}s)"
    log "融合: 硬盘≤${DISK_COOL_THRESHOLD}°C 时,CPU转速上限 ${CPU_NOISE_LIMIT}%"

    cpu_next=0
    disk_next=0
    cpu_temp=0
    disk_temp=0
    cpu_speed=30
    disk_speed=30

    while true; do
        now=$(date +%s)

        # 更新 CPU 状态
        if (( now >= cpu_next )); then
            cpu_temp=$(get_cpu_temp)
            cpu_speed=$(get_speed $cpu_temp CPU_SPEED_MAP)
            cpu_next=$(( now + CPU_INTERVAL ))
            debug_log "CPU updated: ${cpu_temp}°C → ${cpu_speed}%"
        fi

        # 更新硬盘状态(低频)
        if (( now >= disk_next )); then
            disk_temp=$(get_active_disk_max_temp)
            disk_speed=$(get_speed $disk_temp DISK_SPEED_MAP)
            disk_next=$(( now + DISK_INTERVAL ))
            debug_log "Disk updated: max=${disk_temp}°C → ${disk_speed}%"
        fi

        # === 智能融合策略 ===
        if (( disk_temp <= DISK_COOL_THRESHOLD )); then
            # 硬盘凉快,限制 CPU 引起的风扇噪音
            limited_cpu_speed=$(( cpu_speed > CPU_NOISE_LIMIT ? CPU_NOISE_LIMIT : cpu_speed ))
            target_speed=$(( limited_cpu_speed > disk_speed ? limited_cpu_speed : disk_speed ))
        else
            # 硬盘较热,正常取最大值保障散热
            target_speed=$(( cpu_speed > disk_speed ? cpu_speed : disk_speed ))
        fi
        # ===================

        current_speed=$(cat /tmp/fan_current_speed 2>/dev/null || echo "-1")
        if [[ "$current_speed" != "$target_speed" ]]; then
            if [[ -x "$FANCTRL" ]]; then
                "$FANCTRL" "$target_speed" >/dev/null 2>&1
                echo "$target_speed" > /tmp/fan_current_speed
                log "🌡️ CPU:${cpu_temp}°C(${cpu_speed}%) | Disk:${disk_temp}°C(${disk_speed}%) → 🌀 风扇:${target_speed}%"
            else
                log "❌ 错误:fanctrl 工具缺失或不可执行!"
                exit 1
            fi
        else
            debug_log "保持转速: ${target_speed}%"
        fi

        sleep 5
    done
}

main

0

主题

2

回帖

0

牛值

江湖小虾

2025-12-19 14:18:13 显示全部楼层

楼主,fanctrl 工具缺失这个哪里还能下载么?官方论坛里的链接失效了

wget -O /root/fanctrl http://down.sgwbox.com/tools/x86/fanctrl  详情 回复
2025-12-19 14:24

5

主题

20

回帖

0

牛值

江湖小虾

2025-12-19 14:24:17 楼主 显示全部楼层
喵喵喵喵喵 发表于 2025-12-19 14:18
楼主,fanctrl 工具缺失这个哪里还能下载么?官方论坛里的链接失效了

wget -O /root/fanctrl http://down.sgwbox.com/tools/x86/fanctrl
感谢,已经搞好了  详情 回复
2025-12-19 15:15

0

主题

8

回帖

0

牛值

江湖小虾

2025-12-19 14:34:10 显示全部楼层

性价比确实高,但是整体全部是塑料的。。。。

0

主题

2

回帖

0

牛值

江湖小虾

2025-12-19 15:15:49 显示全部楼层
mysxl 发表于 2025-12-19 14:24
wget -O /root/fanctrl http://down.sgwbox.com/tools/x86/fanctrl

感谢,已经搞好了

1

主题

74

回帖

0

牛值

初出茅庐

2025-12-19 19:00:19 显示全部楼层

这是真正的大神呢

5

主题

20

回帖

0

牛值

江湖小虾

2025-12-21 19:54:31 楼主 显示全部楼层

重启后脚本并没有自动启动。

1. 创建风扇控制脚本

bash
编辑
cat > /etc/init.d/fan_auto <<'EOF'
#!/bin/sh
### BEGIN INIT INFO
# Provides:          fan_auto
# Required-Start:    $local_fs $remote_fs $syslog
# Required-Stop:     $local_fs $remote_fs $syslog
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: Fan auto control for SGW-G2
# Description:       Smart fan control based on CPU and disk temp
### END INIT INFO

SCRIPT="/root/fan_auto.sh"
PIDFILE="/var/run/fan_auto.pid"

start() {
    if [ -f "$PIDFILE" ] && kill -0 "$(cat $PIDFILE)" 2>/dev/null; then
        echo "fan_auto already running"
        return 1
    fi
    echo "Starting fan_auto control..."
    nohup $SCRIPT > /dev/null 2>&1 &
    echo $! > "$PIDFILE"
}

stop() {
    if [ -f "$PIDFILE" ]; then
        kill "$(cat $PIDFILE)" 2>/dev/null
        rm -f "$PIDFILE"
        echo "fan_auto stopped"
    else
        echo "fan_auto not running"
    fi
}

case "$1" in
    start)
        start
        ;;
    stop)
        stop
        ;;
    restart)
        stop
        sleep 2
        start
        ;;
    *)
        echo "Usage: $0 {start|stop|restart}"
        exit 1
        ;;
esac
EOF

2. 赋予执行权限

bash
编辑
chmod +x /etc/init.d/fan_auto

3. 注册为系统服务(关键!)

bash
编辑
update-rc.d fan_auto defaults

✅ 这条命令会在 /etc/rc[0-6].d/ 下创建符号链接,确保开机/关机时自动调用。

4. 手动启动测试

bash
编辑
service fan_auto start
# 或
/etc/init.d/fan_auto start

5. 验证进程

bash
编辑
ps aux | grep fan_auto.sh
tail /var/log/fan_auto.log

🔄 重启验证

bash
编辑
reboot

重启后登录,检查:

bash
编辑
ps aux | grep fan_auto.sh   # 应在运行
service fan_auto status     # 可能不支持 status,但 start/stop 有效

❓为什么这次能持久生效?

  • 飞牛的根文件系统虽然是只读的,但 /etc 是 overlay 上层可写部分(因为你在里面看到了 rc.local 等可修改文件)
  • update-rc.d 创建的链接和脚本会保存在可写层
  • 系统启动时会按标准 SysV 流程执行 /etc/init.d/ 脚本

💡 你之前能修改 /etc/rc.local 并保存,就证明 /etc 是可持久化的!


🛠️ 后续管理命令

bash
编辑
# 启动
service fan_auto start

# 停止
service fan_auto stop

# 重启
service fan_auto restart

# 取消自启(如需)
update-rc.d -f fan_auto remove

0

主题

1

回帖

0

牛值

江湖小虾

2025-12-23 09:35:54 显示全部楼层

感谢大神,真的有效果,正好需要!

0

主题

1

回帖

0

牛值

江湖小虾

2025-12-24 19:04:27 显示全部楼层
有没有详细点的操作步骤,不会用啊
弄好了没  详情 回复
5 天前

0

主题

8

回帖

0

牛值

江湖小虾

2025-12-25 14:19:02 显示全部楼层

感谢大佬,照着步骤,已经用上了!

只是后来发现两个问题:

1、当温度在41度左右波动时的时候,风扇转速会频繁在30%,35%反复切换,看log文件,似乎几秒钟就切换一次。

2、本人纯小白,不懂编程,用豆包AI阅读原程序,发现似乎有一处逻辑错误,用元宝AI也确认这一点,

错误截屏如下:

80E77F2B-513B-4C57-B2AC-D7ACCF5C38BD.png

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x

0

主题

8

回帖

0

牛值

江湖小虾

2025-12-25 14:48:20 显示全部楼层

下面是豆包修改后的程序,有需要的可以拿去。。。我已经用上,目前一切正常。

📜 文件名:/root/fan_auto.sh

#!/bin/bash

# 拾光坞 G2 风扇智能调速脚本 v3.2(防抖版)
# ✅ 解决临界温度转速抖动(迟滞逻辑)
# ✅ 分离 CPU / 硬盘控制策略 + 硬盘休眠保护 + 智能融合
# ✅ 兼容低版本 bash | 高温告警 | 调速重试 | 残留清理
# 作者:豆包 | 2025-12-23

# ====== 配置区(可按需调整)======
FANCTRL="/root/fanctrl"
LOGFILE="/var/log/fan_auto.log"
MAX_LOG_LINES=300
DEBUG_LOG=false

# CPU 控制(响应快)
CPU_INTERVAL=15
# CPU 转速映射(升速阈值)+ 降速迟滞(-2℃)
CPU_SPEED_MAP=(
    [0]=30    # ≤39℃ (降速≤38℃)
    [40]=35   # ≥40℃ (降速≤38℃)
    [45]=40   # ≥45℃ (降速≤43℃)
    [50]=50   # ≥50℃ (降速≤48℃)
    [55]=60   # ≥55℃ (降速≤53℃)
    [60]=70   # ≥60℃ (降速≤58℃)
    [65]=80   # ≥65℃ (降速≤63℃)
    [70]=90   # ≥70℃ (降速≤68℃)
    [75]=100  # ≥75℃ (降速≤73℃)
)
CPU_HYSTERESIS=2  # CPU降速迟滞(℃)

# 硬盘控制(保守,仅活跃盘参与)
DISK_INTERVAL=60
DISK_IDLE_THRESHOLD=60  # 60秒无I/O视为休眠
# 硬盘转速映射(升速阈值)+ 降速迟滞(-2℃)
DISK_SPEED_MAP=(
    [0]=30    # ≤39℃ (降速≤38℃)
    [40]=35   # ≥40℃ (降速≤38℃)
    [45]=40   # ≥45℃ (降速≤43℃)
    [50]=50   # ≥50℃ (降速≤48℃)
    [55]=60   # ≥55℃ (降速≤53℃)
    [60]=70   # ≥60℃ (降速≤58℃)
    [65]=80   # ≥65℃ (降速≤63℃)
    [70]=90   # ≥70℃ (降速≤68℃)
    [75]=100  # ≥75℃ (降速≤73℃)
)
DISK_HYSTERESIS=2  # 硬盘降速迟滞(℃)

# 智能融合参数
DISK_COOL_THRESHOLD=45  # 硬盘 ≤ 此温度视为“凉快”
CPU_NOISE_LIMIT=60      # 硬盘凉快时,CPU策略转速上限(%)
MIN_SPEED=30            # 风扇最小转速(防止停转)
HIGH_TEMP_ALARM=80      # 高温告警阈值
# ================================

mkdir -p "$(dirname "$LOGFILE")"

log() {
    local msg="[$(date '+%Y-%m-%d %H:%M:%S')] $*"
    echo "$msg" >> "$LOGFILE"
    tail -n "$MAX_LOG_LINES" "$LOGFILE" > "$LOGFILE.tmp" 2>/dev/null && mv "$LOGFILE.tmp" "$LOGFILE"
}
debug_log() { [[ "$DEBUG_LOG" == "true" ]] && log "DEBUG: $*"; }

# 获取 CPU 温度(恢复 hwmon 兜底)
get_cpu_temp() {
    local max_temp=0
    # 1. thermal_zone
    for zone in /sys/class/thermal/thermal_zone*/; do
        [[ -f "${zone}type" ]] && [[ -f "${zone}temp" ]] || continue
        type=$(cat "${zone}type" 2>/dev/null)
        temp_val=$(cat "${zone}temp" 2>/dev/null)
        temp_c=$((temp_val / 1000))
        if [[ "$type" == *"cpu"* || "$type" == *"core"* || "$type" == *"x86"* ]]; then
            (( temp_c > max_temp )) && max_temp=$temp_c
        fi
    done
    # 2. hwmon 兜底
    if [[ $max_temp -eq 0 ]]; then
        for hwmon in /sys/class/hwmon/hwmon*/; do
            [[ -f "${hwmon}name" ]] || continue
            name=$(cat "${hwmon}name" 2>/dev/null)
            if [[ "$name" == "coretemp" || "$name" == "k10temp" ]]; then
                for input in "${hwmon}"temp*_input; do
                    [[ -f "$input" ]] || continue
                    temp_c=$(( $(cat "$input") / 1000 ))
                    (( temp_c > max_temp )) && max_temp=$temp_c
                done
            fi
        done
    fi
    (( max_temp >= 0 && max_temp <= 100 )) && echo $max_temp || echo 0
}

# 判断硬盘是否活跃(修复临界值逻辑)
is_disk_active() {
    local dev="$1"
    local base_dev=$(basename "$dev")
    local stats_file="/proc/diskstats"

    if ! grep -q "$base_dev" "$stats_file" 2>/dev/null; then
        return 0  # 无法判断,默认视为活跃
    fi

    local current_reads current_writes
    read _ _ _ current_reads _ _ _ current_writes _ < <(awk -v dev="$base_dev" '$3 == dev {print $0}' "$stats_file")

    local last_file="/tmp/fan_disk_${base_dev}.io"
    if [[ -f "$last_file" ]]; then
        read last_reads last_writes last_time < "$last_file"
        local now=$(date +%s)

        if [[ $current_reads != "$last_reads" ]] || [[ $current_writes != "$last_writes" ]]; then
            # I/O变化 → 活跃
            echo "$current_reads $current_writes $now" > "$last_file"
            return 0
        elif (( now - last_time > DISK_IDLE_THRESHOLD )); then
            # I/O无变化且超时 → 休眠
            return 1
        else
            # I/O无变化但未超时 → 活跃
            return 0
        fi
    else
        echo "$current_reads $current_writes $(date +%s)" > "$last_file"
        return 0
    fi
}

# 获取活跃硬盘最高温度
get_active_disk_max_temp() {
    local max_temp=0
    for dev in /dev/sd[a-z] /dev/nvme[0-9]n[0-9]; do
        [[ -e "$dev" ]] || continue
        if is_disk_active "$dev"; then
            debug_log "Disk $dev is ACTIVE"
            local temp=$(smartctl -A "$dev" 2>/dev/null | awk '$1 == "194" {print $10; exit}')
            if [[ -z "$temp" ]] || ! [[ "$temp" =~ ^[0-9]+$ ]]; then
                temp=$(smartctl -A "$dev" 2>/dev/null | awk '/^Temperature:/ && /Celsius/ {print $2; exit}')
            fi
            if [[ "$temp" =~ ^[0-9]+$ ]] && (( temp >= 0 && temp <= 80 )); then
                (( temp > max_temp )) && max_temp=$temp
                debug_log "Disk $dev temp = ${temp}°C"
            fi
        else
            debug_log "Disk $dev is IDLE (skip SMART read)"
        fi
    done
    echo $max_temp
}

# 带迟滞防抖的转速计算(核心修改)
get_speed_with_hysteresis() {
    local temp=$1
    local map_name=$2
    local hysteresis=$3
    local last_speed=$4  # 上一次的转速(用于判断升/降速)
    local speed=30

    # 1. 先获取当前温度对应的“升速阈值”转速
    for t in 75 70 65 60 55 50 45 40 0; do
        if (( temp >= t )); then
            eval "speed=\${${map_name}[$t]}"
            break
        fi
    done

    # 2. 防抖逻辑:仅当温度低于“降速阈值”时,才降低转速
    if (( speed < last_speed )); then
        # 需要降速 → 计算降速阈值(升速阈值 - 迟滞值)
        local down_threshold=0
        for t in 75 70 65 60 55 50 45 40 0; do
            eval "if [[ \${${map_name}[$t]} -eq $last_speed ]]; then down_threshold=$((t - hysteresis)); break; fi"
        done
        # 温度未低于降速阈值 → 保持原转速
        if (( temp > down_threshold )); then
            speed=$last_speed
        fi
    fi

    echo $speed
}

# 主循环
main() {
    # 清理残留的I/O记录文件
    find /tmp -name "fan_disk_*.io" -mtime +7 -delete 2>/dev/null
    find /tmp -name "fan_disk_*.io" -size 0 -delete 2>/dev/null

    log "=== 风扇智能调速 v3.2(防抖版)启动 ==="
    log "策略: CPU(${CPU_INTERVAL}s, 迟滞${CPU_HYSTERESIS}℃) + Disk(${DISK_INTERVAL}s, 迟滞${DISK_HYSTERESIS}℃)"
    log "融合: 硬盘≤${DISK_COOL_THRESHOLD}°C 时,CPU转速上限 ${CPU_NOISE_LIMIT}%"
    log "保护: 最小转速${MIN_SPEED}% | 高温告警${HIGH_TEMP_ALARM}°C"

    cpu_next=0
    disk_next=0
    cpu_temp=0
    disk_temp=0
    cpu_speed=30  # 初始转速
    disk_speed=30 # 初始转速
    last_target_speed=30  # 上一次最终转速(用于防抖)

    while true; do
        now=$(date +%s)

        # 更新 CPU 状态(带防抖)
        if (( now >= cpu_next )); then
            cpu_temp=$(get_cpu_temp)
            # 传入上一次CPU转速,计算带防抖的新转速
            cpu_speed=$(get_speed_with_hysteresis $cpu_temp "CPU_SPEED_MAP" $CPU_HYSTERESIS $cpu_speed)
            cpu_next=$(( now + CPU_INTERVAL ))
            debug_log "CPU updated: ${cpu_temp}°C → ${cpu_speed}% (防抖后)"
        fi

        # 更新硬盘状态(带防抖)
        if (( now >= disk_next )); then
            disk_temp=$(get_active_disk_max_temp)
            # 传入上一次硬盘转速,计算带防抖的新转速
            disk_speed=$(get_speed_with_hysteresis $disk_temp "DISK_SPEED_MAP" $DISK_HYSTERESIS $disk_speed)
            disk_next=$(( now + DISK_INTERVAL ))
            debug_log "Disk updated: max=${disk_temp}°C → ${disk_speed}% (防抖后)"
        fi

        # === 智能融合策略 ===
        if (( disk_temp <= DISK_COOL_THRESHOLD )); then
            # 硬盘凉快,限制 CPU 引起的风扇噪音
            limited_cpu_speed=$(( cpu_speed > CPU_NOISE_LIMIT ? CPU_NOISE_LIMIT : cpu_speed ))
            target_speed=$(( limited_cpu_speed > disk_speed ? limited_cpu_speed : disk_speed ))
        else
            # 硬盘较热,正常取最大值保障散热
            target_speed=$(( cpu_speed > disk_speed ? cpu_speed : disk_speed ))
        fi

        # === 高温告警 + 强制满速 ===
        if (( cpu_temp >= HIGH_TEMP_ALARM || disk_temp >= HIGH_TEMP_ALARM )); then
            log "⚠️ 高温告警!CPU:${cpu_temp}°C | Disk:${disk_temp}°C → 强制转速:100%"
            target_speed=100
        fi

        # === 最小转速保护 ===
        target_speed=$(( target_speed < MIN_SPEED ? MIN_SPEED : target_speed ))

        # === 风扇调速(仅转速真变化时执行)===
        if [[ "$last_target_speed" != "$target_speed" ]]; then
            if [[ -x "$FANCTRL" ]]; then
                retry=0
                while (( retry < 3 )); do
                    "$FANCTRL" "$target_speed" >/dev/null 2>&1
                    if [[ $? -eq 0 ]]; then
                        echo "$target_speed" > /tmp/fan_current_speed
                        log "🌡️ CPU:${cpu_temp}°C(${cpu_speed}%) | Disk:${disk_temp}°C(${disk_speed}%) → 🌀 风扇:${target_speed}%"
                        last_target_speed=$target_speed  # 更新上一次转速
                        break
                    fi
                    (( retry++ ))
                    sleep 1
                done
                if (( retry >= 3 )); then
                    log "⚠️ 风扇调速重试失败!目标转速: ${target_speed}%"
                fi
            else
                log "❌ 错误:fanctrl 工具缺失或不可执行!"
                sleep 60
                continue
            fi
        else
            debug_log "保持转速: ${target_speed}% (温度波动未触发调速)"
        fi

        sleep 5
    done
}

# 脚本异常重启
trap 'log "脚本异常退出,5秒后重启"; sleep 5; exec $0' EXIT HUP INT TERM

main

在配置区有一行: HIGH_TEMP_**=80 # 高温告警阈值 这明显是 Markdown 渲染错误 或 复制粘贴失误,正确应为: HIGH_TEMP_ALARM=80 # 或 HIGH_TEMP_THRESHOLD=80 否则脚本会报错: line XX: HIGH_TEMP_**: co  详情 回复
2025-12-25 21:41

5

主题

20

回帖

0

牛值

江湖小虾

2025-12-25 21:41:45 楼主 显示全部楼层
放大光明 发表于 2025-12-25 14:48
下面是豆包修改后的程序,有需要的可以拿去。。。我已经用上,目前一切正常。
📜 文件名:/root/fan_auto.s ...

在配置区有一行:

HIGH_TEMP_**=80      # 高温告警阈值
这明显是 Markdown 渲染错误 或 复制粘贴失误,正确应为:
HIGH_TEMP_ALARM=80   # 或 HIGH_TEMP_THRESHOLD=80
否则脚本会报错:
line XX: HIGH_TEMP_**: command not found
建议修正为:
HIGH_TEMP_ALARM=80

0

主题

8

回帖

0

牛值

江湖小虾

2025-12-26 10:19:01 显示全部楼层

谢谢大佬提醒,是markdown渲染显示错误,上面源码已进行修改了。

0

主题

13

回帖

0

牛值

系统先锋体验团🛩️

2026-1-12 14:43:11 显示全部楼层

好用。输出到终端更方便查看运行状况

0

主题

1

回帖

0

牛值

江湖小虾

时xx 发表于 2025-12-24 19:04
有没有详细点的操作步骤,不会用啊

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

本版积分规则