基于ESP32与蓝牙串口打造桌面级系统监控屏:硬件选型、通信协议与代码实战 1. 项目概述为什么我们需要一个独立的系统监控屏作为一名常年与电脑打交道的开发者我经常需要同时关注好几个关键的系统指标CPU和GPU的温度、占用率内存使用情况硬盘读写速度甚至后台某个渲染进程的进度。频繁地按“AltTab”切到任务管理器或者让监控软件的小窗口占据宝贵的屏幕空间都挺影响专注度的。于是一个想法冒了出来能不能用一块小巧的屏幕把这些信息实时、直观地展示出来放在显示器旁边想看就瞄一眼ESP32这颗芯片的出现让这个想法变得异常简单。它内置了Wi-Fi和蓝牙性能足够功耗又低简直是这类物联网小项目的“万金油”。这次我选择用蓝牙来连接PC和ESP32原因很简单稳定、低延迟、无需网络环境。Wi-Fi虽然也能做但需要路由器在办公室或固定工位没问题但如果想带着笔记本换个地方用蓝牙配对一次就能用显然更方便。这个项目就是打造一个基于ESP32的蓝牙系统监控屏它从你的电脑获取实时数据并在自带的屏幕上清晰展示。最终成品是一个可以放在桌面的小设备通过USB供电屏幕常亮实时刷新着你关心的系统状态。无论是打游戏时担心硬件过热还是跑代码编译时想看资源占用它都能给你最直观的反馈。2. 核心方案设计与硬件选型解析2.1 整体架构与通信流程拆解整个项目的逻辑链条非常清晰可以分为上位机PC端和下位机ESP32端两部分。上位机数据采集与发送端数据采集在电脑上运行一个后台程序我们称之为“Agent”或“服务端”它负责调用系统API定时比如每秒获取CPU使用率、温度、内存占用、GPU状态等信息。数据处理与封装将采集到的原始数据整理成一个结构化的格式。为了传输高效我选择了JSON。它虽然比纯二进制稍大一点但可读性好在ESP32上解析也方便。蓝牙发送通过电脑的蓝牙模块将封装好的JSON数据包发送给已配对的ESP32设备。下位机数据接收与显示端蓝牙接收ESP32的蓝牙串口SPP模块持续监听接收来自PC的数据包。数据解析将收到的JSON字符串解析成各个独立的变量如cpu_temp,gpu_usage。界面渲染根据解析出的数据在连接的屏幕上绘制出相应的图形和文字比如用进度条表示使用率用数字显示温度用颜色变化绿/黄/红提示状态。为什么选择蓝牙串口SPP而不是低功耗蓝牙BLE这是一个关键选择。BLE是为极低功耗、间歇性数据传输设计的比如手环。而系统监控是持续、高频1秒1次、数据量小但稳定的数据流。蓝牙串口模拟了传统的串行通信提供了稳定的数据流通道编程模型简单就当串口用延迟也更可控。BLE在这类场景下配置更复杂且维持高频率的Notify对双方功耗和稳定性都是考验。因此SPP是这个项目更合适的选择。2.2 硬件清单与选型考量硬件是项目的骨架选对了事半功倍。1. ESP32开发板核心型号选择ESP32 DevKit C V4 或 NodeMCU-32S 这类带USB转串口、引脚引出齐全的板子即可。注意一定要选择支持蓝牙的型号绝大多数都支持。选型理由ESP32双核处理器主频高达240MHz性能足以流畅处理数据解析和界面刷新内置蓝牙4.2支持经典蓝牙SPP丰富的GPIO和SPI/I2C接口方便驱动屏幕社区支持强大Arduino/ESP-IDF生态完善。2. 显示屏幕门面这是影响体验的关键部件。常见的有几种SPI TFT屏幕推荐如ILI9341、ST7789驱动的屏幕。尺寸常见1.3寸、1.54寸、2.0寸、2.4寸。SPI接口只需占用4-6个IO口刷新率足够30-60 fps是平衡性能和接线复杂度的最佳选择。I2C OLED屏幕如SSD1306驱动的0.96寸屏。优点是接口更简单2根线功耗极低。缺点是尺寸小、单色、刷新复杂图形如进度条可能有点慢。适合显示纯数字和简单文字。并行接口屏幕刷新速度最快但需要占用大量IO口通常15个对于本项目杀鸡用牛刀不推荐。我的选择2.4寸ILI9341 SPI TFT屏。理由尺寸够大能同时显示多项信息且清晰色彩丰富可以用颜色编码不同状态如CPU温度用蓝-绿-红渐变SPI驱动成熟有现成的优秀图形库如TFT_eSPI支持。3. 其他必要部件杜邦线若干用于连接ESP32和屏幕。USB数据线Micro-USB或Type-C用于给ESP32供电和烧录程序。可选外壳与支架3D打印一个简单的外壳能让你的监控屏看起来更专业也保护电路。硬件连接示意图以ILI9341 SPI屏为例 屏幕的SPI接口通常需要连接以下引脚到ESP32屏幕引脚ESP32 GPIO引脚功能VCC3.3V 或 5V*电源接3.3V最安全GNDGND地线CSGPIO 5片选Chip SelectRESETGPIO 22复位ResetDC/RSGPIO 21数据/命令选择Data/CommandMOSI (SDI)GPIO 23SPI主设备输出Master OutSCK (CLK)GPIO 18SPI时钟Serial ClockLED3.3V可串联电阻背光控制常开可接3.3V注意有些屏幕逻辑电平是3.3V但背光需要5V驱动请仔细查阅你的屏幕资料。安全起见电源都先接3.3V测试。3. 软件环境搭建与核心代码解析3.1 开发环境与库的配置我们将在Arduino IDE环境下进行开发因为它对ESP32和蓝牙的支持非常友好库生态丰富。安装Arduino IDE从官网下载并安装最新版。添加ESP32开发板支持打开文件 - 首选项在“附加开发板管理器网址”中输入https://espressif.github.io/arduino-esp32/package_esp32_index.json打开工具 - 开发板 - 开发板管理器搜索“esp32”安装“Espressif Systems”提供的包。安装必要的库TFT_eSPI这是驱动SPI TFT屏幕的明星库效率高功能强。通过项目 - 加载库 - 管理库安装。ArduinoJSON用于解析从PC端传来的JSON数据。同样通过库管理器安装。关键一步配置TFT_eSPI库安装后需要在Arduino的库文件夹中找到TFT_eSPI目录编辑里面的User_Setup.h文件。你需要根据你的屏幕型号和接线注释/取消注释相应的驱动和引脚定义。例如对于ILI9341你需要确保#define ILI9341_DRIVER被启用并且下面定义的引脚号TFT_CS,TFT_DC,TFT_RST等与你的实际接线一致。这是屏幕能否点亮的关键。3.2 ESP32端下位机代码核心逻辑ESP32端的代码主要负责蓝牙通信、数据解析和界面绘制。以下是核心逻辑模块的拆解。1. 蓝牙串口初始化与连接管理#include “BluetoothSerial.h” BluetoothSerial SerialBT; void setup() { Serial.begin(115200); // 用于调试输出 SerialBT.begin(“ESP32-System-Monitor”); // 蓝牙设备名称 Serial.println(“蓝牙已启动等待配对连接...”); }在loop()函数中我们需要持续检查是否有数据可读void loop() { if (SerialBT.available()) { String jsonString SerialBT.readStringUntil(‘\n’); // 假设PC端以换行符结束一个数据包 parseAndDisplay(jsonString); } // 可以添加一些UI动画或心跳检测 }实操心得使用readStringUntil(‘\n’)是一种简单的帧分隔方法要求PC端发送的每个JSON包都以换行符结尾。这比单纯用available()读字节更可靠避免了数据包粘在一起的问题。2. JSON数据解析假设PC端发送的数据格式如下{“cpu”: 45.2, “cpu_temp”: 65, “mem”: 76.5, “gpu”: 62.1, “gpu_temp”: 70, “uptime”: “3:14:15”}解析代码如下#include ArduinoJson.h void parseAndDisplay(String jsonStr) { StaticJsonDocument256 doc; // 根据JSON大小调整256字节通常足够 DeserializationError error deserializeJson(doc, jsonStr); if (error) { Serial.print(“JSON解析失败: “); Serial.println(error.c_str()); return; } float cpuUsage doc[“cpu”]; // 45.2 int cpuTemp doc[“cpu_temp”]; // 65 float memUsage doc[“mem”]; // 76.5 // … 获取其他变量 updateDisplay(cpuUsage, cpuTemp, memUsage, …); // 调用显示函数 }3. 屏幕显示界面绘制使用TFT_eSPI库进行绘制。建议将界面布局函数和数据显示函数分开。#include TFT_eSPI.h TFT_eSPI tft TFT_eSPI(); void setupDisplay() { tft.init(); tft.setRotation(1); // 根据屏幕方向调整0-3 tft.fillScreen(TFT_BLACK); // 绘制静态界面标题、标签、进度条边框等 tft.setTextColor(TFT_WHITE, TFT_BLACK); tft.drawString(“System Monitor”, 10, 10, 2); tft.drawString(“CPU:”, 10, 40, 2); tft.drawRect(50, 40, 150, 15, TFT_WHITE); // 进度条边框 // … 绘制其他静态元素 } void updateDisplay(float cpu, int cpuT, float mem) { // 1. 清空动态区域用黑色矩形覆盖旧数据避免残影 tft.fillRect(55, 42, 140, 11, TFT_BLACK); // 清空CPU进度条内部 // 2. 绘制动态进度条 int barWidth map(cpu, 0, 100, 0, 140); // 将百分比映射为像素宽度 uint16_t barColor TFT_GREEN; if (cpu 80) barColor TFT_RED; else if (cpu 60) barColor TFT_YELLOW; tft.fillRect(55, 42, barWidth, 11, barColor); // 3. 更新文本数据 tft.setTextColor(TFT_WHITE, TFT_BLACK); tft.drawString(String(cpu, 1) “%”, 200, 40, 2); // 显示百分比 tft.drawString(String(cpuT) “C”, 200, 60, 2); // 显示温度 // … 更新其他数据 }注意事项频繁刷新全屏会导致闪烁。局部刷新是关键。只重绘变化的部分如进度条填充区域、数字文本而不是整个屏幕。fillRect清空旧区域再画新内容是常用的局部刷新手段。3.3 PC端上位机数据采集程序PC端程序负责采集数据并通过蓝牙发送。你可以用任何熟悉的语言编写如Python、C#、Go等。这里以Python为例因为它跨平台且库丰富。1. 安装Python库pip install psutil pybluezpsutil强大的跨平台系统监控库能轻松获取CPU、内存、磁盘、网络、传感器温度等信息。pybluez或bleak用于蓝牙通信。经典蓝牙SPP推荐使用pybluez在Windows上可能需要额外配置。2. Python数据采集与发送核心代码import psutil import json import time import bluetooth # 来自pybluez def get_system_stats(): 采集系统状态数据 stats { “cpu”: psutil.cpu_percent(interval0.5), # 短暂间隔获取瞬时值 “cpu_temp”: get_cpu_temperature(), # 需要平台特定实现 “mem”: psutil.virtual_memory().percent, “gpu”: get_gpu_usage(), # 需要额外库如pynvml for NVIDIA “gpu_temp”: get_gpu_temperature(), “uptime”: time.strftime(“%H:%M:%S”, time.gmtime(time.time() - psutil.boot_time())) } return {k: (round(v, 1) if isinstance(v, float) else v) for k, v in stats.items()} def get_cpu_temperature(): 获取CPU温度Linux/macOS/Windows方法不同 # Linux示例从/sys/class/thermal读取 # Windows示例可使用pyspectator或OpenHardwareMonitor的WMI接口 # 此处返回一个模拟值实际需要根据平台实现 return 50 def send_via_bluetooth(device_name“ESP32-System-Monitor”, data_dict): 通过蓝牙串口发送数据 # 查找附近的蓝牙设备 nearby_devices bluetooth.discover_devices(lookup_namesTrue) target_addr None for addr, name in nearby_devices: if device_name in name: target_addr addr break if not target_addr: print(“未找到目标设备”) return # 建立RFCOMM连接通道1通常是串口服务 sock bluetooth.BluetoothSocket(bluetooth.RFCOMM) sock.connect((target_addr, 1)) # 发送JSON数据以换行符结尾 json_str json.dumps(data_dict) “\n” sock.send(json_str) sock.close() if __name__ “__main__”: while True: stats get_system_stats() print(f“采集到数据{stats}”) try: send_via_bluetooth(“ESP32-System-Monitor”, stats) except Exception as e: print(f“蓝牙发送失败{e}”) time.sleep(1) # 每秒发送一次重要提示获取GPU信息和CPU温度是平台相关的在Windows上可能需要pynvmlNVIDIA或GPUtil库以及访问WMI。在Linux上可以从/sys/class或/proc文件系统读取。这部分需要根据你的操作系统进行额外研究和代码编写。4. 系统集成、调试与优化实战4.1 烧录、配对与首次运行烧录ESP32程序用USB线连接ESP32和电脑。在Arduino IDE中选择正确的开发板型号如ESP32 Dev Module和端口将完整的代码整合了蓝牙和显示部分编译上传。PC端蓝牙配对打开电脑的蓝牙设置。将ESP32上电你应该能在“添加蓝牙设备”列表中看到一个名为“ESP32-System-Monitor”或你自定义的名称的设备。点击配对。通常ESP32端不需要输入密码如果电脑提示可以尝试输入“0000”或“1234”。运行PC端程序首先确保蓝牙已连接。然后以管理员/普通权限运行你的Python数据发送脚本。观察脚本输出看是否有“发送成功”的提示同时观察ESP32的串口监视器波特率115200看是否在接收并解析JSON数据。观察屏幕如果一切顺利屏幕应该会亮起并开始显示从电脑传来的动态数据。4.2 常见问题与排查技巧实录在实际搭建过程中你几乎一定会遇到下面这些问题。这里是我的踩坑记录和解决方案。问题现象可能原因排查步骤与解决方案屏幕一片空白或花屏1. 电源问题电压/电流不足2. 接线错误或接触不良3. TFT_eSPI库配置错误1.先查电源确保VCC接3.3V/5VGND接GND。尝试单独给屏幕外接供电测试。2.再查接线逐根检查杜邦线尤其是SCK、MOSI、CS、DC、RST这几根。最好用万用表通断档检查。3.最后查软件仔细核对User_Setup.h中驱动型号和引脚定义是否与你的屏幕和接线完全一致。ESP32蓝牙找不到或配对失败1. 蓝牙未初始化或名称冲突2. 电脑蓝牙驱动问题3. 防火墙或安全软件拦截1. 检查ESP32代码中SerialBT.begin()是否执行。尝试修改蓝牙设备名称确保唯一。2. 重启电脑蓝牙服务更新蓝牙驱动。3. 在电脑上删除已配对的ESP32设备重新搜索配对。临时关闭防火墙试试。PC程序能发现设备但连接失败1. RFCOMM通道号不对2. ESP32蓝牙服务未就绪1. 大多数蓝牙串口使用通道1。可以尝试通道1-30。在ESP32代码中可以初始化多个服务但SerialBT默认通道是1。2. 确保ESP32程序已完全启动并执行到SerialBT.begin()。查看串口监视器是否有蓝牙启动日志。数据能收到但显示乱码或解析失败1. 串口波特率不匹配仅指调试串口2. JSON格式错误或数据包不完整3.ArduinoJSON文档容量不足1. 确保PC端发送和ESP端SerialBT读取的编码一致都是UTF-8。2.在ESP32端将收到的原始字符串打印到串口监视器这是最关键的调试手段查看字符串是否是一个完整的、格式正确的JSON。PC端确保每个包以\n结尾。3. 增大StaticJsonDocument256中的容量比如改成512。屏幕刷新闪烁严重1. 刷新区域计算错误导致全屏刷新2. 刷新频率过高1. 检查updateDisplay函数中的fillRect坐标和大小确保它只覆盖需要更新的区域不要误擦背景。2. 适当降低数据发送和刷新频率比如从1秒一次改为2秒一次。获取不到CPU/GPU温度1. 平台不支持2. 权限不足3. 依赖库未安装1.Linux检查/sys/class/thermal/thermal_zone*/temp文件是否存在可读。2.Windows需要管理员权限运行脚本。考虑使用OpenHardwareMonitor并搭配其WMI接口或使用pyspectator库。3.NVIDIA GPU确保安装了pynvml库和NVIDIA驱动。独家避坑技巧分阶段调试不要试图一次性写完所有代码。先让ESP32的屏幕显示静态文字测试屏幕驱动再让蓝牙回传一个固定字符串测试蓝牙通信最后再整合JSON解析和动态显示。善用串口监视器这是你调试ESP32的“眼睛”。把关键变量、接收到的原始数据、错误信息都打印出来。电源隔离如果屏幕工作时ESP32出现重启很可能是电源电流不足。尝试用外部5V电源如手机充电器通过VIN引脚给ESP32供电或者使用带外部供电的USB HUB。JSON容量预留StaticJsonDocument的大小宁大勿小。计算一下你的JSON字符串最大可能长度并留出至少50%的余量防止解析溢出导致奇怪错误。4.3 界面美化与功能扩展思路基础功能跑通后你可以让它变得更酷更实用。界面美化使用字体TFT_eSPI支持加载自定义字体.vlw格式。可以找一些小的像素字体让显示更精致。绘制图表除了进度条可以绘制简单的折线图显示最近一段时间如60秒内CPU使用率的变化趋势。动画效果在数据刷新时添加简单的过渡动画比如数字滚动、进度条平滑增长。功能扩展显示更多信息网络上传/下载速度、磁盘剩余空间、当前时间、天气信息需Wi-Fi等。交互控制为ESP32增加一两个按钮或电容触摸点通过蓝牙向PC发送指令比如一键静音、切换音频输出设备、打开某个应用。多屏切换长按按钮切换不同的信息面板比如一屏显示系统监控一屏显示日历/待办事项。离线告警在ESP32端设置阈值如CPU温度80℃当数据超限时让屏幕闪烁红色边框或背光即使PC端程序崩溃也能警示。性能优化双核利用ESP32有两个核心。可以将蓝牙数据接收解析放在一个核心Core 0将屏幕刷新放在另一个核心Core 1使用FreeRTOS的任务Task进行管理使响应更流畅。数据压缩如果觉得JSON传输效率低可以定义简单的二进制协议用几个字节表示各种状态大幅减少数据量。这个项目从想法到实现最享受的就是那种“软硬结合”的成就感。看着自己桌上这个小屏幕实时跳动着电脑的脉搏不仅是个实用的工具更是一个极客的桌面装饰。它不复杂但涵盖了物联网开发的几个核心环节硬件选型、嵌入式编程、无线通信和桌面应用开发。当你成功让它运行起来之后那些调试时遇到的坑都会变成宝贵的经验。

本月热点