尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
ESP32+BLE+JSON:全栈自造桌面状态仪表盘实战
1. 项目缘起为什么我要给自己造一块桌面仪表盘做开发的人大概都有过这种体验本地跑着三四个服务编译进度、接口状态、消息队列积压、CI 流水线结果全散落在不同的终端窗口和浏览器标签页里。想瞄一眼当前状态得来回切窗口切着切着注意力就散了。我试过用手机当第二屏、试过各种桌面小组件最后发现都不够顺手——要么太占地方要么刷新不及时要么配置复杂到比写业务代码还累。于是就有了这个Status Deck项目一块巴掌大的桌面仪表盘放在显示器旁边实时显示我关心的几组数据。它的核心思路很简单——用 ESP32 做硬件终端通过 BLE低功耗蓝牙和桌面端 Agent 通信Agent 负责采集本机各种状态打包成 JSON 推给硬件硬件解析后渲染到小屏幕上。整套东西不依赖云端不走公网纯本地闭环隐私和延迟都可控。这个标题里的“全栈自造”不是噱头。从硬件选型、固件开发、BLE 协议设计到桌面端 Agent 的数据采集和 JSON 序列化再到屏幕 UI 的布局逻辑每一层都得自己动手。适合谁来参考我觉得有三类人值得往下看一是手里有 ESP32 开发板、想找个正经项目练手的嵌入式新手二是做后端或客户端开发、想给工位加点“物理外挂”的工程师三是对 BLE 通信、JSON 数据交换、固件烧录这些环节感兴趣但一直没找到完整落地案例的折腾党。我踩过的坑不少BLE 连接不稳定、JSON 字段对不上、屏幕刷新撕裂、烧录时串口被占用……这些后面都会展开讲。先把整体设计思路说清楚不然后面的细节容易变成无根之木。2. 整体架构与方案选型为什么是 ESP32 BLE JSON2.1 硬件选型ESP32 凭什么胜出市面上能做桌面小仪表盘的方案不少我对比过几种主流路线。ESP32 的优势在于三点自带 Wi-Fi 和 BLE 双模、GPIO 资源够用、社区生态成熟。如果只用 Wi-Fi配网和 IP 变动会带来额外复杂度如果只用普通蓝牙经典模式功耗和连接管理又不如 BLE 省心。ESP32 的 BLE 协议栈在 Arduino 环境和 ESP-IDF 下都有稳定实现这对快速迭代很关键。屏幕我选的是 1.8 寸 TFT SPI 屏分辨率 128x160驱动芯片 ST7735。为什么不用更大的屏因为桌面仪表盘的核心是“瞥一眼就能获取信息”不是拿来看视频。小屏反而逼着你把信息层级做清楚。供电直接用 USB 线从显示器背后的接口取电省掉额外电源适配器。注意ESP32 开发板型号很多建议选带外部天线接口或 PCB 天线设计成熟的版本。我第一块板子 BLE 信号在金属显示器旁边衰减严重换了板子才稳定。2.2 通信方案BLE 而不是 Wi-Fi 或串口这里重点解释为什么选 BLE。串口方案最直接但线缆束缚太强桌面走线一乱就难受。Wi-Fi 方案带宽大但需要配网、处理 IP 变化、还要考虑局域网防火墙对一个“只传几百字节状态”的场景来说太重。BLE 方案刚好卡在中间无需配网、点对点连接、功耗低、桌面端和 ESP32 都能原生支持。具体来说ESP32 作为 BLE 外设Peripheral桌面端 Agent 作为中心设备Central。Agent 扫描到指定服务 UUID 后发起连接然后通过特征值Characteristic写入 JSON 数据。ESP32 收到数据后解析并刷新屏幕。整个链路单向为主偶尔需要反向确认时再加一个通知特征值。2.3 数据格式JSON 的取舍与字段设计热词里反复出现 JSON这不是偶然。JSON 的可读性和跨语言支持是它最大的优势。桌面端 Agent 可能用 Python、Node.js 或 Go 写ESP32 端用 CJSON 是大家都能轻松解析的中间格式。但 ESP32 内存有限不能像服务器那样随便解析大 JSON所以字段设计必须克制。我定的原则是扁平结构、短键名、固定字段、数值优先。比如状态数据不写成嵌套对象而是直接平铺{cpu:23,mem:61,net:up,ci:pass,msg:3}这样在 ESP32 端用 ArduinoJson 库解析时内存占用可控。如果字段嵌套太深动态分配会碎片化内存跑久了容易崩。后面会详细讲解析时的内存管理技巧。3. 核心细节拆解从固件到 Agent 的关键环节3.1 ESP32 固件BLE 服务定义与数据接收固件部分我用 Arduino 框架开发原因是上手快、库齐全。核心是定义一个 BLE 服务包含一个可写特征值用于接收 JSON一个可读特征值用于返回设备状态。服务 UUID 和特征值 UUID 可以自己生成只要桌面端和固件端一致即可。关键代码结构大致是这样初始化 BLE 设备、创建服务、创建特征值、设置回调。当桌面端写入数据时回调函数被触发把数据拷贝到缓冲区然后交给解析逻辑。这里有个细节BLE 回调运行在协议栈上下文不能在里面做耗时操作否则会阻塞连接。我的做法是回调里只做数据拷贝和置标志位主循环检测到标志位后再解析和刷新屏幕。// 回调中只拷贝数据不解析 class StatusCallback : public BLECharacteristicCallbacks { void onWrite(BLECharacteristic *pChar) { std::string value pChar-getValue(); if (value.length() 0) { memcpy(jsonBuffer, value.c_str(), value.length()); jsonBuffer[value.length()] \0; newDataFlag true; } } };缓冲区大小要提前规划。我一开始设了 128 字节结果状态字段一多就截断。后来改成 256 字节并加了长度检查防止溢出。这个数值不是拍脑袋来的按每个字段平均 12 字节、最多 15 个字段估算加上括号和逗号200 字节左右比较稳妥。3.2 桌面端 Agent数据采集与 JSON 序列化Agent 我用 Python 写跑在后台每隔几秒采集一次系统状态。采集项包括 CPU 使用率、内存占用、网络连通性、CI 最近一次结果、未读消息数等。这些数据来源不同有的读系统接口有的调本地命令有的查本地缓存文件。采集完之后统一组装成 JSON。这里要注意数值精度和类型CPU 百分比取整避免浮点数在 ESP32 端解析时占用额外内存布尔值用 0/1 表示比 true/false 更省字节字符串尽量用短枚举比如网络状态用 up/down 而不是完整句子。import json, psutil status { cpu: int(psutil.cpu_percent()), mem: int(psutil.virtual_memory().percent), net: up if check_net() else down, ci: get_ci_status(), msg: get_unread_count() } payload json.dumps(status, separators(,, :))separators参数去掉多余空格能省几个字节。别小看这几个字节BLE 单次传输有 MTU 限制默认 23 字节协商后通常能到 185 字节左右。数据越紧凑分包概率越低。3.3 屏幕渲染信息层级与刷新策略128x160 的屏幕能显示的内容有限所以布局必须做减法。我的方案是分三行第一行显示 CPU 和内存用进度条加数字第二行显示网络和 CI 状态用图标加文字第三行显示消息数用大号数字。颜色区分状态正常绿色、警告黄色、异常红色。刷新策略上不要每次收到数据就全屏重绘。全屏重绘会闪而且 SPI 写入耗时。我的做法是对比新旧数据只更新变化的区域。比如 CPU 从 23 变到 24只重绘那个数字所在的矩形区域。这需要记录每个字段的屏幕坐标和上一次的值。实操心得TFT 屏幕的 SPI 频率可以适当调高但太高会出现花屏。我实测 ST7735 在 27MHz 下稳定40MHz 偶尔出问题。如果屏幕线较长降到 20MHz 更保险。4. 实操过程从零搭建的完整步骤4.1 开发环境准备与离线包处理Arduino IDE 对 ESP32 的支持需要额外安装开发板包。网络条件好的时候直接在线安装就行但热词里提到“arduino ide esp32离线包”说明很多人遇到下载慢的问题。我的建议是优先用官方板管理器 URL如果反复失败再考虑离线包。离线包版本要和 IDE 版本匹配否则会出现工具链路径错误。安装完成后在开发板管理器里选择对应的 ESP32 型号。烧录方式我常用的是 USB 转串口注意有些板子需要按住 BOOT 键再点上传。如果遇到“Failed to connect”或“Timed out waiting for packet header”先检查串口驱动再检查波特率最后检查 USB 线——有些线只能供电不能传数据这个坑我踩过不止一次。4.2 BLE 连接建立与参数协商ESP32 烧录好固件后会广播 BLE 服务。桌面端 Agent 用 bleak 库Python扫描并连接。连接建立后双方会协商 MTU。MTU 越大单次能传的数据越多。ESP32 端可以主动请求更大的 MTU桌面端也要配合。我实测协商到 185 字节比较稳再大有些适配器会拒绝。连接参数也值得调。BLE 连接间隔Connection Interval影响延迟和功耗。桌面仪表盘对延迟敏感我把间隔设短一些比如 30ms 到 50ms。但太短会增加功耗和丢包率需要平衡。这些参数在 ESP32 端通过BLEDevice::setMTU()和连接更新请求来设置。BLEDevice::init(StatusDeck); BLEDevice::setMTU(185);4.3 JSON 解析与内存管理ESP32 端解析 JSON 用 ArduinoJson 库。这个库有静态和动态两种模式。静态模式StaticJsonDocument在栈上分配速度快但容量固定动态模式DynamicJsonDocument在堆上分配灵活但容易碎片化。我的数据量小且字段固定所以用静态模式容量设 256 字节。解析时要检查返回值确认没有解析错误。如果字段缺失或类型不对要有兜底逻辑不能让屏幕显示乱码。比如 CPU 字段解析失败时显示 -- 而不是 0这样一眼就能看出是数据问题而不是真的 CPU 空闲。StaticJsonDocument256 doc; DeserializationError err deserializeJson(doc, jsonBuffer); if (!err) { int cpu doc[cpu] | -1; // 更新屏幕 }4.4 烧录与调试的现场记录烧录时串口监视器是主要调试手段。我习惯在关键节点打日志BLE 初始化完成、连接建立、数据收到、解析成功、屏幕刷新。日志用Serial.println()输出波特率 115200。注意串口打印本身也耗时不要在 BLE 回调里打日志否则会拖慢协议栈。调试 BLE 时桌面端可以用扫描工具确认服务是否广播、特征值是否可写。如果连不上先确认 UUID 是否一致再确认设备是否已被其他中心设备占用。BLE 外设通常只能被一个中心设备连接如果手机还连着桌面端就连不上。5. 常见问题与排查技巧实录5.1 BLE 连接不稳定怎么办这是最高频的问题。表现是连接几秒后断开或者数据写入失败。排查顺序如下先看距离ESP32 和桌面端蓝牙适配器之间不要隔金属再看干扰USB 3.0 接口和 Wi-Fi 路由器附近 BLE 容易受干扰然后看连接参数间隔太短或监督超时太短都会导致误判断开。我的经验是把监督超时Supervision Timeout设大一些比如 4 秒给足重传机会。同时桌面端 Agent 要加自动重连逻辑断开后隔几秒重新扫描连接不要一次失败就退出。5.2 JSON 字段对不上或解析失败常见原因是桌面端改了字段名但固件端没同步或者数值类型变了比如从整数变成浮点数。建议在项目里维护一份字段定义文档两端都按文档来。另外JSON 里不要出现中文或特殊字符ESP32 端解析可能出问题。如果必须传字符串确保是 ASCII。还有一个隐蔽的坑JSON 末尾多了换行符或空格某些解析库会报错。桌面端序列化后最好strip()一下再发送。5.3 屏幕刷新异常与花屏处理花屏通常和 SPI 时序有关。检查接线是否松动、频率是否过高、电源是否稳定。ESP32 的 3.3V 输出给屏幕供电时如果屏幕背光电流大可能导致电压跌落进而花屏。解决办法是屏幕背光单独供电或者加电容滤波。刷新撕裂则是刷新策略问题。如果全屏重绘时数据正好在更新就会出现半新半旧的画面。用局部刷新可以缓解但根本方案是双缓冲——不过 ESP32 内存有限128x160 的双缓冲要 40KB比较紧张。我的折中是只对变化区域做刷新并且刷新前先关掉屏幕输出刷完再打开。5.4 烧录失败与串口占用烧录失败最常见的原因是串口被占用。Arduino IDE 的串口监视器如果开着上传就会失败。另外某些桌面端 Agent 如果也在读串口同样会冲突。烧录前先关闭所有可能占用串口的程序。如果提示“No serial data received”检查板子是否进入下载模式。有些板子需要手动按住 BOOT 键有些自动下载电路设计有缺陷需要手动复位。热词里提到“esp32自动下载电路”确实有些廉价板子的自动下载电路不稳定手动操作反而更可靠。问题现象可能原因排查方法解决手段BLE 连不上UUID 不一致用扫描工具确认广播核对两端 UUID连接后秒断连接参数过激查看断开原因码增大监督超时JSON 解析失败字段类型变化打印原始数据统一字段定义屏幕花屏SPI 频率过高降低频率测试降到 20MHz烧录超时串口被占用关闭监视器释放串口后重试独家避坑ESP32 的 BLE 和 Wi-Fi 共用射频如果固件里同时开了 Wi-FiBLE 稳定性会下降。Status Deck 不需要 Wi-Fi所以我在固件里彻底关掉了 Wi-Fi只留 BLE连接稳定性明显提升。6. 后续扩展与个人体会这套 Status Deck 目前跑在我工位上已经几个月了最直观的感受是不用再切窗口看状态注意力碎片少了很多。它不解决什么大问题但确实让日常开发舒服了一点。后续我打算加几个扩展一是用外部中断接一个物理按键按一下切换显示页面比如从系统状态切到 CI 详情二是把 Agent 做成跨平台的现在只支持本机以后想通过局域网收集其他机器的状态三是屏幕 UI 再打磨一下加个简单的动画过渡让刷新不那么生硬。如果你也想动手做我的建议是先跑通最小闭环ESP32 能广播、桌面端能连上、能传一个字段、屏幕能显示。这个闭环通了后面加字段、加页面、加逻辑都是顺水推舟。最怕一上来就设计大而全的协议结果卡在 BLE 连接上几天没进展热情就磨没了。另外固件烧录和调试阶段串口日志是你的好朋友但别依赖它做实时输出。真正跑起来之后日志要能关掉否则影响性能。我在固件里加了一个编译开关调试版本打日志发布版本静默这样两不耽误。
RELATED

相关推荐

黄金矿工Java小游戏:Swing游戏循环与状态机实现

黄金矿工Java小游戏:Swing游戏循环与状态机实现

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📅 2026/10/30 3:59:53
EMC整改实战:辐射发射、传导发射与静电抗扰度排查思路

EMC整改实战:辐射发射、传导发射与静电抗扰度排查思路

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📅 2026/10/30 3:54:53
嵌入式开发从裸机到Linux的四阶段工程路线图

嵌入式开发从裸机到Linux的四阶段工程路线图

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📅 2026/10/30 3:54:53
MORE NEWS

更多资讯

📰

SMT来料条码与UID设计:从供应商标签到一物一码

SMT 工厂来料普遍面临标签标准化难题:每家元器件供应商的料盘标签格式各不相同。同一料号,A 供应商使用带批次信息的 Code128 一维码;B 供应商采用 Datamatrix 二维码附带日期码;C 供应商物料数量印刷在独立副标签上。 仓库系统只…

📰

NIR 穿透 PBT 与带状光缆毫秒级飞拍方案

1. 光学问题 有些光通信行业的客户会问到松套管(Loose Tube)能不能不剥管,直接隔着管子把里面 12 芯光纤的颜色顺序识别出来? 这个问题听起来简单,实际上是个光学问题,不是算法问题。 光缆里的松套管或束管…

📰

医疗大模型落地元年:小白程序员必看,收藏这份保姆级指南助力职业腾飞!

医疗大模型已进入理性增长期,政策、技术、资本三重驱动应用落地。院内以信息化升级为主,院外ToG、ToB、ToC模式各具特色。未来将深耕专科,多元融合,C端向全周期健康陪伴转型。本文剖析商业模式、落地痛点,助你把握趋势…

📰

小白程序员必备:收藏这四种大模型知识组织与检索模式,轻松构建企业知识底座

本文介绍了企业构建知识底座的重要性,并详细解析了四种知识组织与检索模式:向量RAG、GraphRAG、PageIndex和LLM-Wiki。文章从实际应用场景出发,分析了每种模式的原理、优缺点和适用性,并探讨了多种模式的融合应用方法。通过学习本…

📰

教培出海系统的 AI 能力落地架构:客服、排课、内容与学习分析

学页官方技术号 出海办学技术指南系列 第 2 篇(第 1 篇:四项技术底座) 先泼个冷水:市面上多数教育系统的"AI",拆开看是第三方大模型 API 的浅封装。功能清单写了十几个,工程上真正落到教育场景的…

📰

HR把这个背下来,你的工作汇报就厉害了!

做HR,最怕的不是工作没做好,而是明明忙了一个月,到了汇报的时候,却不知道该怎么讲。 招聘岗说这个月招了多少人,考勤岗说处理了多少异常,培训岗说组织了几场活动。每个人都汇报了自己的工作,可老…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬