用 ESP32 给笔记本做了套蓝牙温控风扇:温度自己上报,转速自己调
-
给笔记本做了一套 ESP32 蓝牙温控风扇:电脑自己上报温度,风扇自己调速,不用的时候自动停下来。
折腾这个主要是嫌原来的散热底座太吵 —— 不管电脑烫不烫,扇子从头转到尾。改成 ESP32 控速之后,轻载时基本听不见声音,跑重活才拉起来。
实际使用演示
️ 效果预览
整体就是一台压风式散热器 + 一块 ESP32 控制板。PC 上的小工具读取 CPU / GPU 温度,通过蓝牙发过来,板子按温度调风扇转速,OLED 上实时显示两路温度和当前转速。
系统组成
整套分三段:
- PC 端 — Windows 上的常驻小工具,读温度、发数据
- ESP32 端 — 接收温度、算转速、驱动风扇、刷新屏幕
- 执行部分 — MOSFET 驱动电路 + PWM 风扇
通信走蓝牙串口,数据是纯文本格式,一行一条:
CPU52.3 GPU47.8板子收到合法数据会回一个
ACK。这个确认很重要 —— 蓝牙串口经常出现「端口能打开、数据也能写进去,但对端根本没反应」的假连接,有没有ACK是判断链路真假的唯一依据。
️ 它是怎么工作的
从温度到转速的完整链路:
- PC 端读取 CPU 和 GPU 温度,各取自身最高的传感器读数
- 每 2 秒通过蓝牙发送一次
- ESP32 校验格式和取值范围,通过后回
ACK - 取两路温度里较高的那个,查温度曲线得到目标转速
- 按限速规则平滑逼近目标,输出 PWM
- 屏幕同步刷新
温度到转速的对应关系是条连续曲线,中间做线性插值:
温度 55°C 60°C 65°C 70°C 75°C 80°C 转速 16% 23% 31% 40% 75% 100% 70°C 以下走得很平缓,日常办公基本在 20%~30% 之间,几乎听不见;75°C 开始往上拉,80°C 直接满速。曲线陡的那段留给真正需要散热的时候。
转速变化有限速:升速每秒最多 10%,降速每秒最多 3%。降速比升速慢是刻意的 —— 温度刚掉一点就把风撤掉,容易来回震荡,听感上就是风扇忽快忽慢。
️ PC 端做了什么上位机是个 Python 小工具,打包成单文件,常驻系统托盘。
温度读取
调用 OpenHardwareMonitor 读取硬件传感器。CPU 上通常有好几个温度点(核心、封装、主板),取最高的那个更保险,避免低估了真正的热点。GPU 则同时兼容 NVIDIA / AMD / Intel 三家。读数超出 0~120°C 范围的一律丢掉,防止传感器抖动把异常值传下去。
找串口
不会死等某一个固定的 COM 口。优先用配置里指定的,找不到就按设备特征匹配,再找不到就扫所有带蓝牙字样的串口,最后兜底遍历全部可用端口。
打不开、写超时,或者连上了但收不到
ACK,都会把这个口标记成「坏的」,冷却 30 秒以上再跳过,去试下一个。有意思的是,一旦某个口回了ACK,它的冷却标记会立刻解除 —— 说明它其实是好的,先前的失败可能只是暂时的。休眠唤醒后自动恢复
电脑睡一觉醒来,蓝牙串口的状态其实是坏的 —— 端口句柄还在,底层链路已经断了。程序靠循环间隔判断这件事:间隔超过 8 秒就认为刚从休眠恢复,于是主动断开重连、重新初始化硬件监控,最长给 20 秒的恢复时间。
开机自启
首次运行会自动写开机启动项,不需要管理员权限。程序换目录了会自动更新路径;发现别处有指向自己的重复启动项也会顺手清掉。用源码方式跑的时候不动启动项,避免把开发机搞乱。
托盘图标
托盘图标本身就是一张实时温度曲线 —— 最近 30 个点连成的折线,按温度区间变色(低是绿色、中间橙色、高了红色),下面还标着 CPU 和 GPU 的整数温度。不用点开窗口,瞄一眼托盘就知道风扇跟没跟上。
ESP32 端做了什么固件只做温控这一件事 —— 按键、手动模式、自定义曲线全部去掉了。目标是装好之后就不用再碰它。
数据校验
每一行都严格检查:前缀必须是
CPU或GPU、数值要能完整解析、数字后面不能拖多余字符、必须在 0~120°C 之间。任何一项不过关,整行丢掉,等下一帧。蓝牙是流式传输,一帧可能被拆成几段到达。固件只认完整的换行符;如果缓冲区里有半截数据、超过 1 秒没等到后续,就把这半截丢掉重来 —— 不会把两次会话的数据拼在一起,也不会因为一个半包把接收卡死。
两路温度分开计时
CPU 和 GPU 各自独立计时,超过 12 秒没更新就判定这一路失效。这么做是因为有些平台的独显在休眠时会停报温度 —— 如果两路共用一个超时,CPU 明明正常也会被 GPU 拖累成「失效」,把风扇误停了。分开之后,GPU 掉线不影响 CPU 继续控温。
平滑调速
目标转速不会一步到位,而是每 100ms 往目标靠一点,配合前面说的升降速限制。温差超过 80°C 时例外 —— 直接满速,不讲平滑,先压温度。
另外风扇冷启动时先用 30% 转 1 秒再进入曲线,因为有些风扇占空比太低根本起转。
自动休眠
这是我最想要的一个功能。判断分两层:
- 蓝牙客户端消失 —— 电脑关机、断连、走远了,风扇立刻停
- 数据超时 —— 连接还在但 12 秒没数据,同样停
风扇是立刻停的,屏幕则要显示一个 3、2、1 的倒计时再关。多亮这 3 秒是有用的:能让人看明白设备是「因为没数据才停的」,而不是以为它死机了。倒计时只在一次有效通信结束时触发一次,不会反复闪。
蓝牙自动重连
蓝牙偶发假死时,固件有一次有限度的自恢复:只在曾经通过信、且连续 60 秒收不到数据时触发,重启一次蓝牙协议栈然后等电脑自己连回来。
这里有个我踩过的坑 —— 早期版本掉线后会反复重启蓝牙,结果电脑正常休眠(本来就是该断开)时,风扇端却在不停重启、反复广播。现在的策略是「断开了就安静等着」,电脑醒了会自己连回来,设备不需要做多余动作。一个通信周期内只恢复一次。
残留数据清理
这个是解决一个很具体的故障:快速重连的时候,新连接可能读到上一次会话的残留数据,导致风扇按错误的旧温度跑。
现在每次检测到断开(包括断开恰好发生在快速重连期间的情况),都会清掉两路温度的有效标志、清空接收缓冲、把蓝牙缓存里剩的字节一次性读空。之后只接受新到达的帧。
OLED 显示
128×64 的屏幕,左右两栏大字显示 CPU 和 GPU 温度,底下一行是连接状态(
CON)、当前转速(SPEED xx%)和模式(QUIET)。屏幕刷新 200ms,温度无效时显示--。顺带一提,OLED 初始化失败的话温控照常跑 —— 显示只是附属功能,不该影响散热。
硬件接线ESP32 引脚 接到哪 GPIO 5风扇 MOS 管栅极(PWM 信号) 5VOLED VCC GND共地 GPIO 21OLED SDA GPIO 22OLED SCL PWM 频率用 15kHz,超出人耳敏感范围,能少一点驱动噪声。
风扇一定要经过 MOSFET 驱动,GPIO 只能出毫安级的信号,直接带不动风扇。另外 ESP32 和风扇电源必须共地。
更新记录2026.09.11 首次发布- ESP32 固件:保留 Quiet 自动温控,移除按键与手动模式
- 新增自动休眠:无连接 / 无数据自动停风扇、关屏
- 新增蓝牙自动连接:断线后单次恢复,不周期性重启
- 新增残留数据清理:断开即清,只接收新帧
- 上位机:Python 重写,托盘常驻 + 实时温度曲线
- 温控曲线:七锚点连续插值,80°C 满速
️ 项目地址完整源码与可执行文件都在仓库里
https://github.com/gukey/ESP32-CPU-FAN
仓库里有完整的 ESP32 固件源码、PC 端源码、编译好的可执行文件、依赖 DLL,以及测试代码。想自己改的话直接拿源码改就行。
刷写教程和接线细节仓库里都有说明,硬件部分照着接就行。
️ 注意事项- 刷固件时一定要先断开散热器的 12V 电源线。USB 的 5V 和 12V 同时接上会冲突,可能烧电脑 USB 控制器甚至烧板子。
- 上位机是没签名的程序,Windows 或杀毒软件可能报警。介意的话可以先看源码再运行。
- 温度数据来自硬件传感器,受驱动和主板差异影响可能有偏差。这只是散热辅助,不能替代主板自身的过热保护。
- 风扇的最低起动占空比各不相同,如果 30% 转不起来,需要在源码里把这个值调高重新烧录。
关于来源这套东西的原始方案来自 究物研社 —— 压风式散热器的思路、风扇电路模块都是他们做的。ESP32 温控部分是 DALIAN213 的方案。
我这个版本是在他们的基础上继续改的,主要补充了自动休眠、蓝牙自动连接和残留数据清理这几块。借这个机会谢谢两位。

请使用微信扫一扫