
1. 为什么我会盯上 PyBLE 这个项目第一次在 GitHub 上刷到 PyBLE 的时候我正蹲在工位上给一块 ESP32-S3 调 BLE 服务桌上摊着两块开发板、一根 USB 线、一台笔记本外加一个用来供电的充电宝。那会儿我脑子里冒出来的第一个念头就是如果我能直接拿手里的平板通过蓝牙连上 ESP32在平板上写 MicroPython 代码、跑 REPL、看报错那该省多少事。PyBLE 干的就是这件事——它是一个跑在 Android 平板或手机上的 MicroPython IDE通过 BLE低功耗蓝牙跟 ESP32 建立串口式连接让你在没有数据线、没有电脑的场景下照样能写代码、传文件、开终端。这个项目解决的核心痛点其实很具体。传统玩 ESP32 的流程是电脑装 Arduino IDE 或者 Thonny插 USB 线选串口烧固件再开 REPL。这套流程在固定工位上没问题但一旦你想把 ESP32 塞进一个已经装好的外壳里、挂在墙上、埋进某个传感器节点再想插线调试就非常难受。PyBLE 把“调试入口”从 USB 物理接口换成了 BLE 无线通道只要 ESP32 上跑着带 BLE 串口服务的 MicroPython 固件平板就能连上去。适合谁适合做物联网原型、做现场调试、做教学演示、以及像我这样懒得每次插线的人。哪怕你之前只用过 Arduino IDE只要对 MicroPython 有一点点概念这篇内容都能让你把整套流程跑通。2. 整体设计思路与方案选型拆解2.1 为什么是 BLE 而不是 Wi-Fi 或经典蓝牙很多人第一反应是ESP32 明明有 Wi-Fi为什么不用 Wi-Fi 做无线调试我一开始也这么想后来实际对比过才发现Wi-Fi 方案在“随手调试”这个场景里有几个绕不开的坑。第一Wi-Fi 连接需要配网要么开热点要么连路由器平板和 ESP32 得在同一网段现场环境一变就得重新配。第二Wi-Fi 功耗高对于电池供电的节点开着 Wi-Fi 等调试会迅速掉电。第三Wi-Fi 的 TCP 连接建立慢平板端要维护 socket断线重连逻辑复杂。BLE 的优势在于连接建立快通常一两秒内就能完成功耗低对电池友好而且 BLE 的 GATT 服务模型天然适合做“串口透传”——把数据拆成一个个 characteristic 的读写操作。PyBLE 正是利用了 BLE 上的 Nordic UART ServiceNUS或者自定义的串口服务把 MicroPython 的 REPL 输入输出封装成 BLE 数据包。平板端看起来是在跟一个串口打交道底层其实是 BLE 的 write 和 notify。至于经典蓝牙SPPESP32 虽然支持但在 Android 上权限和兼容性更麻烦而且功耗也比 BLE 高。所以选 BLE 是综合了功耗、连接速度、跨平台兼容性之后最稳的方案。2.2 MicroPython 固件为什么要带 BLE 串口服务PyBLE 能工作的前提是 ESP32 上跑的 MicroPython 固件里已经启用了 BLE 串口服务。官方 MicroPython 的 ESP32 固件默认不一定开 BLE你需要自己编译或者找已经带ubluetooth模块的固件。我实测下来最省事的方式是直接用社区维护的带 BLE 的固件或者自己用mpy-cross加esp-idf编译。固件里要做的核心事情是初始化 BLE 栈注册一个 UART 服务把收到的数据转发给 MicroPython 的 stdin把 stdout 的数据通过 notify 发出去。这里有个关键点MicroPython 的 REPL 默认走 UART0也就是 USB 串口你要让它同时走 BLE就得在main.py或者启动脚本里做重定向。常见做法是用os.dupterm()把 BLE 的流对象挂到 REPL 上。这样平板通过 BLE 发过去的字符就等同于你在串口终端里敲的字符。2.3 平板端为什么选 Android 而不是 iOSPyBLE 目前主要在 Android 上跑原因很现实Android 对 BLE 的权限控制相对开放而且文件系统访问更自由方便你做文件上传下载。iOS 的 BLE 权限和后台限制更严加上 App 分发成本高所以这个项目优先覆盖 Android 平板。如果你手里是 iPad也不是完全没戏但需要自己折腾不在这次讨论范围内。2.4 整体架构一句话说清ESP32 跑 MicroPython BLE UART 服务平板跑 PyBLE App两者通过 BLE GATT 建立双向数据通道PyBLE 把这条通道包装成“串口终端 文件管理器 代码编辑器”。你写代码、点运行、看输出全部在平板上完成ESP32 只负责执行。3. 核心细节解析与实操要点3.1 ESP32 端固件准备别急着刷先确认这三件事第一件事确认你的 ESP32 型号。ESP32、ESP32-S3、ESP32-C3 都支持 BLE但固件不通用。我踩过的坑是拿 S3 的固件刷到普通 ESP32 上结果 BLE 初始化直接报错。第二件事确认固件里有没有ubluetooth模块。刷完之后在 REPL 里敲import ubluetooth不报错才算过关。第三件事确认 BLE 串口服务的 UUID。PyBLE 默认会扫描特定的服务 UUID如果你自己改过要在 App 里手动填。提示刷固件之前先用esptool.py flash_id确认芯片型号和 flash 大小避免刷错。3.2 BLE 串口服务的参数怎么定BLE 串口透传的核心参数有三个服务 UUID、TX characteristic UUID、RX characteristic UUID。TX 是 ESP32 发给平板的notifyRX 是平板发给 ESP32 的write。常见的一套是 Nordic UART Service 的 UUID服务6E400001-B5A3-F393-E0A9-E50E24DCCA9ETX6E400003-B5A3-F393-E0A9-E50E24DCCA9ERX6E400002-B5A3-F393-E0A9-E50E24DCCA9EMTU 也很关键。默认 BLE MTU 是 23 字节实际可用载荷 20 字节。如果你要传大文件得在连接后协商更大的 MTUAndroid 端一般能到 247 或 512。PyBLE 在连接时会尝试请求 MTUESP32 端也要在gatts_set_buffer里设置足够大的缓冲区。我实测 MTU 设 247 时传一个 10KB 的main.py大概两三秒够用。3.3 平板端 PyBLE 的界面逻辑PyBLE 的界面分三块顶部是连接状态和扫描按钮中间是文件列表和编辑器底部是 REPL 输出。连接成功后你可以浏览 ESP32 文件系统里的文件点开编辑保存后直接写回。REPL 区域可以敲命令也可以看print输出。这里有个细节PyBLE 写文件不是覆盖整个文件系统而是通过 MicroPython 的os接口逐块写入所以大文件写入时不要中途断开 BLE否则文件会残缺。3.4 代码编辑与运行的闭环在 PyBLE 里写代码的体验接近桌面 IDE有基本的高亮和缩进。你写完main.py后可以点“运行”让 ESP32 重启并执行。也可以直接在 REPL 里import某个模块测试。我常用的流程是先在 REPL 里逐行调通逻辑再整理成文件保存最后重启验证。这样比反复刷固件快得多。3.5 文件传输的坑与技巧BLE 传文件最怕的是丢包和断连。我的经验是单次写入不要超过 MTU 减去 3 字节的开销每写一块等一个确认再写下一块传之前先os.stat看剩余空间。另外MicroPython 的文件系统写入速度有限传大文件时平板端要加延时不然 ESP32 端缓冲区溢出会丢数据。4. 实操过程与核心环节实现4.1 第一步给 ESP32 刷入带 BLE 的 MicroPython 固件先去 MicroPython 官网下载对应型号的固件如果你需要 BLE 支持建议找带ubluetooth的每日构建版或者社区版。刷写命令如下esptool.py --chip esp32 --port /dev/ttyUSB0 erase_flash esptool.py --chip esp32 --port /dev/ttyUSB0 --baud 460800 write_flash -z 0x1000 esp32-ble-20240101.bin刷完后用串口终端连上去敲import ubluetooth确认模块存在。然后写一个ble_uart.py内容大致是初始化 BLE、注册服务、把收到的数据写到sys.stdin把sys.stdout重定向到 BLE notify。这个脚本可以放在main.py里开机自启。4.2 第二步配置 BLE 串口服务并测试在 ESP32 上跑起 BLE 服务后用手机上的通用 BLE 调试 App 先扫一遍确认能看到你的设备名和 UART 服务。如果能连上并收到 notify说明 ESP32 端没问题。这一步很关键因为如果直接上 PyBLE 连不上你分不清是 ESP32 端还是平板端的问题。先用通用 App 排除 ESP32 端故障再调 PyBLE。4.3 第三步平板安装 PyBLE 并连接PyBLE 可以从 GitHub Releases 下载 APK 安装。打开后授予蓝牙和位置权限Android 扫描 BLE 需要位置权限。点扫描找到你的 ESP32 设备名点连接。连接成功后界面会显示文件列表。如果列表为空说明 BLE 串口服务通了但文件系统接口没对接好检查os.listdir是否正常。4.4 第四步写一个 Blink 测试闭环在 PyBLE 编辑器里新建blink.pyimport machine import time led machine.Pin(2, machine.Pin.OUT) while True: led.value(not led.value()) time.sleep(0.5)保存后点运行看板子上的 LED 是否闪烁。如果闪了说明整条链路通了。如果不闪先在 REPL 里手动敲led.value(1)看有没有反应逐步缩小范围。4.5 第五步传一个稍大的项目文件找一个 5KB 左右的main.py通过 PyBLE 的文件上传功能传进去。传的过程中观察 REPL 有没有报错。传完后重启看程序是否正常跑。我实测传 5KB 文件大约 1 到 2 秒传 20KB 文件大约 5 到 8 秒。如果超过 10 秒还没传完检查 MTU 是否协商成功。4.6 参数计算MTU 与传输时间估算假设 MTU 协商为 247 字节实际每包载荷约 244 字节。传 20KB 文件需要 20000 / 244 ≈ 82 包。BLE 连接间隔假设 30ms每包一个往返理论时间约 82 × 30ms ≈ 2.5 秒。实际因为有重传和协议开销乘以 2 到 3 倍大约 5 到 8 秒跟实测吻合。如果你把连接间隔调到 15ms时间还能再短但功耗会上升。5. 常见问题与排查技巧实录5.1 扫描不到设备怎么办先确认 ESP32 的 BLE 服务是否在广播。用通用 BLE App 扫如果也扫不到说明 ESP32 端广播没开。检查代码里有没有ble.gap_advertise。如果通用 App 能扫到但 PyBLE 扫不到检查 PyBLE 的服务 UUID 过滤设置可能它只显示带特定服务的设备。5.2 连接后立刻断开最常见的原因是 MTU 协商失败或者服务 UUID 不匹配。先在 ESP32 端打印连接事件看有没有_IRQ_CENTRAL_CONNECT。如果有连接事件但马上断开检查gatts_set_buffer的缓冲区大小太小会导致协议栈崩溃。5.3 REPL 没输出检查os.dupterm是否挂载成功。可以在串口终端里敲os.dupterm()看返回值。如果返回 None说明没挂上。另外BLE notify 需要平板端先使能 CCCD客户端特征配置描述符PyBLE 一般会自动做但如果没做ESP32 发的数据平板收不到。5.4 文件写入一半失败多半是 BLE 断连或者缓冲区溢出。建议在写入循环里加time.sleep_ms(10)给 ESP32 端留出处理时间。另外写入前先删掉旧文件避免追加模式导致文件错乱。5.5 常见问题速查表现象可能原因排查方法扫描不到设备广播未开或 UUID 过滤用通用 BLE App 交叉验证连接后立刻断开MTU 协商失败或缓冲区小打印连接事件调大缓冲区REPL 无输出dupterm 未挂载或 CCCD 未使能检查 dupterm 返回值文件写入失败断连或溢出加延时分块写入运行代码无反应文件未保存或未重启手动 REPL 测试5.6 独家避坑技巧第一ESP32 的 BLE 和 Wi-Fi 共用射频同时开 Wi-Fi 和 BLE 时 BLE 吞吐会下降调试时尽量关 Wi-Fi。第二平板端不要同时连多个 BLE 设备Android 的 BLE 栈在多连接时容易出问题。第三传文件时把平板屏幕保持常亮息屏可能导致 BLE 连接被系统挂起。第四如果反复连不上重启 ESP32 比重启平板更快恢复。6. 这套方案还能怎么扩展PyBLE 目前主要做调试和轻量文件传输但它的架构可以延伸出更多玩法。比如你可以把 ESP32 采集的传感器数据通过 BLE 实时推到平板用平板做可视化也可以反过来平板当遥控器通过 BLE 给 ESP32 发指令控制继电器。再进一步如果你有多块 ESP32可以做一个 BLE 网关平板连网关网关再通过 BLE 或串口连其他节点。我在实际使用中最大的体会是无线调试一旦用顺了就回不去插线了。尤其是做现场部署的时候平板往兜里一揣走到设备旁边就能改代码这种自由度是传统方式给不了的。当然BLE 的带宽和稳定性跟 USB 比还是有差距大工程还是建议用有线但日常调试和小文件更新PyBLE 完全够用。