如何用Python(PyCharm)将文件拆分为数据包并通过BLE发送?求PC到设备OTA固件更新场景的对应Python脚本示例
使用Python(PyCharm)实现BLE文件分包发送与OTA固件更新
Hey,我来帮你搞定这个PC到BLE设备的OTA固件更新需求——拆分文件、通过BLE发送数据包的逻辑其实很清晰,下面我一步步给你讲清楚,再附上可用的Python脚本。
核心实现思路
要完成这个任务,我们需要拆成两个关键环节:文件拆分包和BLE数据传输,两者配合才能实现可靠的OTA推送。
1. 文件拆分包逻辑
BLE有MTU(最大传输单元)限制,默认一般是23字节,去掉BLE协议层的开销后,实际能传输的有效数据大概在20字节左右(具体数值要看你的设备支持的MTU)。所以我们需要:
- 把固件文件按这个有效载荷大小切割成多个数据包
- 给每个数据包添加必要的控制信息:比如数据包序号(用于设备端重组)、结束标识(告诉设备这是最后一包)
- 计算总包数,确保所有数据都能被完整拆分
2. BLE通信实现
在PC上用Python做BLE客户端,最方便的库是bleak——跨平台支持Windows、macOS、Linux,在PyCharm里直接用pip install bleak就能安装。我们需要:
- 扫描并连接到目标BLE设备
- 找到设备上专门用于OTA的服务和可写特征值(这些UUID需要从你的设备固件文档里获取)
- 逐个发送拆分好的数据包,同时给设备留一点处理时间
Python脚本示例(PC端OTA发送)
下面是一个完整的脚本,你可以根据自己的设备参数修改配置部分:
import asyncio from bleak import BleakClient, BleakScanner import os # -------------------------- 配置参数(根据你的设备修改) -------------------------- TARGET_DEVICE_ADDRESS = "AA:BB:CC:DD:EE:FF" # 目标BLE设备的MAC地址 OTA_SERVICE_UUID = "0000FFE0-0000-1000-8000-00805F9B34FB" # 设备OTA服务的UUID OTA_CHARACTERISTIC_UUID = "0000FFE1-0000-1000-8000-00805F9B34FB" # OTA可写特征值的UUID MTU_PAYLOAD_SIZE = 20 # 每个数据包的有效数据大小,根据设备MTU调整 # ------------------------------------------------------------------------------- async def split_and_send_firmware(file_path): # 检查固件文件是否存在 if not os.path.isfile(file_path): print(f"❌ 错误:找不到固件文件 {file_path}") return # 读取固件二进制数据 with open(file_path, "rb") as firmware_file: firmware_bytes = firmware_file.read() total_bytes = len(firmware_bytes) total_packets = (total_bytes + MTU_PAYLOAD_SIZE - 1) // MTU_PAYLOAD_SIZE # 向上取整计算总包数 print(f"📦 固件信息:大小 {total_bytes} 字节,将拆分为 {total_packets} 个数据包") # 扫描目标BLE设备 print("🔍 正在扫描目标BLE设备...") scanned_devices = await BleakScanner.discover() target_device = None for dev in scanned_devices: if dev.address.upper() == TARGET_DEVICE_ADDRESS.upper(): target_device = dev break if not target_device: print(f"❌ 未找到目标设备 {TARGET_DEVICE_ADDRESS}") return # 连接设备 print(f"🔌 已找到设备 {target_device.name},正在连接...") async with BleakClient(target_device) as client: if not client.is_connected: print("❌ 设备连接失败") return print("✅ 设备连接成功,开始发送数据包...") # 逐个发送数据包 for packet_num in range(total_packets): # 计算当前包的字节范围 start_idx = packet_num * MTU_PAYLOAD_SIZE end_idx = min(start_idx + MTU_PAYLOAD_SIZE, total_bytes) payload = firmware_bytes[start_idx:end_idx] # 构建完整数据包:2字节序号(大端) + 1字节结束标识(0=未结束,1=最后一包) + 有效数据 packet_header = packet_num.to_bytes(2, byteorder='big') end_flag = b'\x01' if packet_num == total_packets - 1 else b'\x00' full_packet = packet_header + end_flag + payload # 发送数据包到BLE特征值 await client.write_gatt_char(OTA_CHARACTERISTIC_UUID, full_packet) print(f"📤 已发送数据包 {packet_num+1}/{total_packets},长度:{len(full_packet)} 字节") # 延迟一小段时间,防止设备处理不过来(根据设备性能调整) await asyncio.sleep(0.01) print("🎉 所有数据包发送完成!请等待设备完成固件烧录和重启。") if __name__ == "__main__": # 替换为你的固件文件路径 FIRMWARE_PATH = "./firmware_v1.0.bin" asyncio.run(split_and_send_firmware(FIRMWARE_PATH))
关键注意事项
- MTU调整:如果你的设备支持更大的MTU,可以先通过
client.mtu = 512(举个例子)来协商更大的传输单元,这样能减少数据包数量,提升传输速度。 - 设备端配合:这个脚本是PC端的发送逻辑,设备端必须实现对应的接收、重组、烧录逻辑——比如根据数据包序号存储数据,收到结束标识后合并所有数据并写入Flash。
- 权限问题:Linux运行时可能需要蓝牙权限,你可以用
sudo执行脚本;macOS需要允许PyCharm/终端访问蓝牙;Windows一般无需额外配置。 - 测试建议:先用小文件(比如100字节的测试文件)验证通信正常,再用实际固件测试,避免浪费时间。
内容的提问来源于stack exchange,提问作者GHAZI JALLOULI




