|
14| 0
|
[K10项目分享] 行空板 K10 打造桌面级性能仪表盘 |
|
本帖最后由 topdog 于 2026-6-25 01:08 编辑 一行命令,把电脑的 CPU、内存、显卡温度、网速全部搬到一块 240×320 的小屏幕上实时跳动。 ## 1. 计算机信息呈现桌面级性能仪表盘 我用python psutil库提取pc运行参数,结合行空板 K10板(ESP32-S3 + ILI9341 屏幕),把它变成一个摆在桌上的性能监视器。三天写代码加两天跟 Windows 温度 API 死磕之后,呈现的效果如下: ![]() ## 2. 整体架构 ``` ┌──────────────────────┐ WiFi HTTP POST ┌────────────────────┐ │ PC (Python/psutil) │ ──────────────────────▶ │ K10 (ESP32-S3) │ │ │ /stats 端点 │ │ │ CPU 使用率/频率 │ │ WebServer :80 │ │ CPU 温度 (WMI) │ │ ILI9341 240×320 │ │ GPU 温度 (nvidia) │ │ LovyanGFX 渲染 │ │ RAM / Disk / Net │ │ efontCN_16 中文 │ │ 电池 / 运行时长 │ │ NVS 存 WiFi 配置 │ └──────────────────────┘ └────────────────────┘ ``` PC 端每秒用 **psutil** 采一次数据,打包成 JSON,HTTP POST 到 K10 的 `/stats`。K10 收到马上刷新屏幕。没什么花哨的。 ## 3. Python 端:psutil 怎么采数据 ### 3.1 psutil 能拿到什么 `psutil` 是 Python 下最老的系统监控库,跨平台,一行 `pip install psutil` 就能用。这个项目用到的 API: | API | 拿什么 | 实际输出 | |-----|-------|---------| | `psutil.cpu_percent(interval=0.5)` | CPU 使用率 | `12.3` (%) | | `psutil.cpu_freq()` | CPU 频率 | `3200` (MHz) | | `psutil.virtual_memory()` | 内存总量/已用 | `63.4GB / 14.7GB` | | `psutil.disk_usage('C:\\')` | 磁盘用量 | `48.3%` | | `psutil.net_io_counters()` | 网络累计字节 | 两次采样做差 | | `psutil.sensors_battery()` | 电池百分比/充电状态 | `71%, plugged` | | `psutil.boot_time()` | 开机时间戳 | 算出运行时长 | 注意 `cpu_percent` 需要 `interval=0.5` 才会有准确读数,否则第一次返回 0。`net_io_counters` 返回的是累计值,算实时速率需要两次采样相减除以时间间隔,这个搞嵌入式计时的应该秒懂。 完整采集函数大概长这样: ```python def get_stats(): cpu_percent = psutil.cpu_percent(interval=0.5) cpu_freq = int(psutil.cpu_freq().current) cpu_temp = _get_cpu_temp() # 下面细说 gpu_temp = _get_gpu_temp() # 下面细说 mem = psutil.virtual_memory() ram_pct = round(mem.percent, 1) ram_used = round(mem.used / 1024**3, 1) ram_total = round(mem.total / 1024**3, 1) disk = psutil.disk_usage('C:\\') battery = psutil.sensors_battery() bat_pct = int(battery.percent) if battery else -1 return {"cpu": cpu_percent, "freq": cpu_freq, ...} ``` ### 3.2 温度采集,这才是最坑的 #### CPU 温度 跑第一版的时候,控制台安静地打印 `cpu_temp=-1`。看了一眼代码,`psutil.sensors_temperatures()` 返回空。查了才知道,**psutil 7.x 把这个函数给移除了**。 然后我就开始了漫长的 Windows WMI 探险: 第一站:`MSAcpi_ThermalZoneTemperature`,在 `root/wmi` 命名空间下。API 很标准,返回值是 0.1K。但一跑就报 `拒绝访问`。需要管理员权限。 第二站:翻微软文档翻到了 `Win32_PerfFormattedData_Counters_ThermalZoneInformation`,在 `root/cimv2` 下面。不用管理员。返回值是 **0.1°C**(注意单位,不是 0.1K),直接 `/10.0` 就是摄氏度。凌晨两点终于看到 `temp=32` 的时候,那种感觉懂的都懂。 于是温度采集变成了三级回退: ```python def _get_cpu_temp(): # 1. psutil 老版本 try: return int(psutil.sensors_temperatures()['coretemp'][0].current) except: pass # 2. MSAcpi — 要管理员 out = subprocess.run(['powershell', '-Command', '(Get-CimInstance -Namespace root/wmi ' '-ClassName MSAcpi_ThermalZoneTemperature).CurrentTemperature'], capture_output=True, text=True) if out.stdout.strip(): return int(float(out.stdout.strip()) / 10.0 - 273.15) # 3. ThermalZoneInformation — 不!用!管!理!员! out = subprocess.run(['powershell', '-Command', '(Get-CimInstance -Namespace root/cimv2 -ClassName ' 'Win32_PerfFormattedData_Counters_ThermalZoneInformation).Temperature'], capture_output=True, text=True) if out.stdout.strip(): return int(float(out.stdout.strip()) / 10.0) # 0.1°C return -1 ``` #### GPU 温度 这个简单到离谱。只要你装了 NVIDIA 驱动,一行 `nvidia-smi` 搞定: ```python def _get_gpu_temp(): out = subprocess.run( ['nvidia-smi', '--query-gpu=temperature.gpu', '--format=csv,noheader'], capture_output=True, text=True ) return int(out.stdout.strip()) if out.stdout.strip() else -1 ``` 没有 NVIDIA 显卡就返回 `-1`,K10 那端看到 `-1` 就不画 GPU 温度。不会崩。 ## 4. K10 端:固件做了什么 硬件就一块行空板 K10(ESP32-S3, 16MB Flash, 8MB PSRAM),屏幕 ILI9341 240×320,背光走 XL9535。K10 固件主要干三件事: 1. **WebServer** 在 80 端口跑着,收 PC 的 `/stats` POST 请求,收到就刷屏 2. **LovyanGFX** 驱动屏幕,`efontCN_16` 内置中文字体画标签,英文数字画进度条和数值 3. **配网系统** 独立跑一套 WiFi 管理逻辑,下面细说 K10 代码已经不短了(编译出来 1.28MB),好在合并固件直接烧,不用从头搭。如果感兴趣细节,项目里有完整 `.ino` 源码。 ## 5. 配网:扫码太麻烦,那就扫 WiFi 传统 ESP32 配网就两种路子:SmartConfig(用手机 App 广播 WiFi 密码,时灵时不灵)、手动敲 SSID(手机键盘打中文 WiFi 名,谁试谁知道)。PC Pulse 用的是第三种:**AP 回退 + 网页扫描选网**。 ### 5.1 整体流程 K10 上电后的 WiFi 决策树: ``` 上电 → 读 NVS 里的 WiFi 配置 ├─ 有配置 → WiFi.begin() 连路由器 │ ├─ 连上了 → 正常模式,屏幕显示 IP │ └─ 连不上(超时 15 秒)→ 开 AP └─ 没配置 → 开 AP(首次使用) ``` AP 热点固定 `PC-Pulse`,密码 `12345678`。手机连上后打开 `192.168.4.1` 就能配网。配完点保存,K10 把 SSID 和密码写入 NVS 的 `k10mon` 命名空间,然后 `ESP.restart()` 自动重启,下次上电走第一条分支直连路由器。 ### 5.2 K10 端两个 API 配网页不是写死的 HTML,是前后端分离的: **`GET /scan`** — 调 `WiFi.scanNetworks()` 同步扫描周围 WiFi,返回 JSON 数组: ```json [ {"s": "MyWiFi_5G", "r": -45, "e": 1}, {"s": "TP-LINK_2.4G", "r": -72, "e": 1}, {"s": "Starbucks", "r": -88, "e": 0} ] ``` 三个字段的意思:`s` = SSID,`r` = 信号强度 RSSI(越接近 0 越强),`e` = 是否需要密码(1 加密 / 0 开放)。最多返回 30 个,`WiFi.scanDelete()` 及时释放内存。 **`GET /saved`** — 从 NVS 里读出上一次保存的 SSID: ```json {"ssid": "MyWiFi_5G", "has": true} ``` 页面加载时先调这个,如果有已存的 SSID 就直接预填到输入框里,省得再扫一遍。 ### 5.3 配网页的前端 整个 HTML 页面塞在 `.ino` 的 `PROGMEM` 区,不占 ESP32 的运行内存。打开以后做的事: 1. 页面加载完,先 `fetch('/saved')` 查有没有存过的 SSID,有就填上 2. 然后 `fetch('/scan')` 拿到 WiFi 列表,遍历渲染 3. 每条 WiFi 用三个指标展示:**SSID 名称**、**信号强度**(用 CSS 画绿/黄/红点的简版信号条)、** ## 6. 上手流程 三步走:烧固件 → 配网拿 IP → 启动 PC 端。 ### 6.1 下载固件 合并好的固件已上传到天翼云盘,直接下载烧录即可: > 链接:https://cloud.189.cn/t/J3aIbmniEFfa > 访问码:`uax0` 文件是 `PC_Pulse_K10_merged.bin`,4MB,含 bootloader + 分区表 + 完整固件。 ### 6.2 烧录 用 esptool 写 `0x0` 地址就行。Windows 用户打开 ESP32 烧录工具(如 ESP Flash Download Tool),选 ESP32-S3、地址填 `0x0`,加载合并固件,点 Start。 命令行的话: ```bash esptool.py --chip esp32s3 --port COM7 write_flash 0x0 PC_Pulse_K10_merged.bin ``` 烧完 K10 自动重启,屏幕亮起。 ### 6.3 配网 首次上电 K10 自动开 AP 热点(`PC-Pulse` / `12345678`)。手机连上去,浏览器打开 `192.168.4.1`,扫一圈选你的 WiFi,输密码,保存。K10 自动重启连上路由器,**屏幕上会显示 IP 地址**(比如 `192.168.3.53`)。 > 配网细节见第 5 节,全程不用敲一行代码。 ### 6.4 启动 PC 端 拿到 K10 的 IP 之后,在 PC 上: ```bash cd PC_Pulse uv sync uv run python main.py 192.168.3.53 # 换成屏幕上的 IP ``` 环境变量也可以:`K10_IP`, `K10_PORT`, `UPDATE_INTERVAL`。跑起来后 PC 端每秒推一次数据,K10 屏幕实时刷新。 ## 7. 花了两天踩出来的经验 - **psutil 版本**:升级到 7.x 之后 `sensors_temperatures()` 就没了。官方没在任何 changelog 里提,我是对着空返回值发了一下午呆才发现的。 - **Windows WMI 有两个坑**:`root/wmi` 下面的类大多要管理员权限,而且单位是 0.1K 不是 0.1°C。`root/cimv2` 下的 `ThermalZoneInformation` 两个问题都没有,一开始不知道,走了一大圈弯路。 - **LovyanGFX** 的 `efontCN_16` 是白送的,不用另外搞字库。16px 在 240px 宽的屏幕上刚好,再大就挤了。 - **配网**:扫列表比手工输入好一万倍。而且把 HTML 放 PROGMEM 里完全不占运行内存,WiFi 扫描 API 返回 JSON 而不是 HTML 片段,前后端分得清清楚楚。 |
沪公网安备31011502402448© 2013-2026 Comsenz Inc. Powered by Discuz! X3.4 Licensed