Micropython BLE实战:3步搞定ESP32与手机蓝牙通信(附完整代码)
MicroPython BLE实战3步实现ESP32与手机的高效蓝牙通信蓝牙低功耗BLE技术已经成为物联网设备通信的主流选择之一。对于使用MicroPython的开发者来说ESP32开发板因其内置BLE功能而成为理想硬件平台。本文将带你快速实现ESP32与手机之间的双向数据通信无需深入底层协议细节三步即可完成从配置到实际数据传输的全过程。1. 环境准备与基础配置在开始BLE通信前我们需要确保开发环境正确配置。ESP32开发板需要刷入支持BLE的MicroPython固件推荐使用最新稳定版本。通过以下命令检查固件是否包含BLE模块import bluetooth print(bluetooth.__name__) # 应输出ubluetooth或bluetooth如果出现ImportError则需要重新刷写固件。对于常见的ESP32-WROOM-32开发板我们可以使用esptool工具进行刷写esptool.py --chip esp32 --port /dev/ttyUSB0 erase_flash esptool.py --chip esp32 --port /dev/ttyUSB0 --baud 460800 write_flash -z 0x1000 esp32-ble-firmware.bin硬件连接只需一根Micro USB数据线用于供电和串口通信。推荐使用Thonny IDE作为开发环境它提供了方便的MicroPython文件管理和REPL交互界面。提示首次使用前建议执行import machine; machine.freq(240000000)将ESP32 CPU频率设置为最高确保BLE通信稳定性。2. 构建BLE外围设备ESP32在BLE通信中通常作为外围设备Peripheral手机则作为中央设备Central。我们需要创建一个BLE服务包含可读Notify和可写Write特征。2.1 定义UUID和服务BLE通信基于GATT协议每个服务和特征都有唯一的UUID。我们使用标准的Nordic UART ServiceNUSUUID这是蓝牙串口协议的通用实现import bluetooth import struct from micropython import const # 定义BLE常量 _IRQ_CENTRAL_CONNECT const(1) _IRQ_CENTRAL_DISCONNECT const(2) _IRQ_GATTS_WRITE const(3) # 定义UART服务UUID _UART_UUID bluetooth.UUID(6E400001-B5A3-F393-E0A9-E50E24DCCA9E) _UART_TX_UUID bluetooth.UUID(6E400003-B5A3-F393-E0A9-E50E24DCCA9E) # 发送特征 _UART_RX_UUID bluetooth.UUID(6E400002-B5A3-F393-E0A9-E50E24DCCA9E) # 接收特征2.2 创建BLE服务类封装BLE操作为一个类简化后续使用class SimpleBLE: def __init__(self, nameESP32-BLE): self._ble bluetooth.BLE() self._ble.active(True) self._ble.irq(self._irq_handler) self._name name self._connections set() self._write_callback None # 注册UART服务 self._uart_service ( _UART_UUID, ( (_UART_TX_UUID, bluetooth.FLAG_READ | bluetooth.FLAG_NOTIFY), (_UART_RX_UUID, bluetooth.FLAG_WRITE | bluetooth.FLAG_WRITE_NO_RESPONSE), ), ) ((self._tx_handle, self._rx_handle),) self._ble.gatts_register_services((self._uart_service,)) # 开始广播 self._advertise() def _irq_handler(self, event, data): if event _IRQ_CENTRAL_CONNECT: conn_handle, _, _ data self._connections.add(conn_handle) elif event _IRQ_CENTRAL_DISCONNECT: conn_handle, _, _ data self._connections.remove(conn_handle) self._advertise() elif event _IRQ_GATTS_WRITE: conn_handle, value_handle data if value_handle self._rx_handle and self._write_callback: self._write_callback(self._ble.gatts_read(self._rx_handle)) def _advertise(self, interval_us500000): payload self._generate_advertising_payload() self._ble.gap_advertise(interval_us, adv_datapayload) def _generate_advertising_payload(self): payload bytearray() # 添加标志 payload struct.pack(BB, 2, 0x01) struct.pack(B, 0x06) # 添加设备名称 if self._name: payload struct.pack(B, len(self._name) 1) struct.pack(B, 0x09) self._name.encode() # 添加服务UUID payload struct.pack(B, 2 1) struct.pack(B, 0x07) _UART_UUID.bin_val[:2] return payload def send(self, data): for conn_handle in self._connections: self._ble.gatts_notify(conn_handle, self._tx_handle, data) def on_receive(self, callback): self._write_callback callback def is_connected(self): return len(self._connections) 03. 实现双向通信3.1 ESP32端数据收发创建主程序文件main.py实现数据收发逻辑from ble_simple import SimpleBLE import utime ble SimpleBLE(ESP32-UART) def on_data_received(data): print(Received:, data) # 示例将接收到的数据回传 if ble.is_connected(): ble.send(bEcho: data) ble.on_receive(on_data_received) counter 0 while True: if ble.is_connected(): # 每隔1秒发送一次计数 ble.send(fCount: {counter}.encode()) counter 1 utime.sleep(1)3.2 手机端连接与测试对于Android用户推荐使用nRF Connect或BLE Scanner等应用iOS用户可以使用LightBlue。连接步骤如下打开BLE扫描工具搜索名为ESP32-UART的设备连接设备后查找UUID为6E400001-B5A3-F393-E0A9-E50E24DCCA9E的服务启用通知Notify特性UUID以...03结尾向写入特性UUID以...02结尾发送数据3.3 微信小程序集成微信小程序提供了完整的BLE API可以实现与ESP32的通信。关键代码如下// 初始化蓝牙模块 wx.openBluetoothAdapter({ success: function(res) { console.log(蓝牙适配器初始化成功) startDiscovery() } }) // 开始搜索设备 function startDiscovery() { wx.startBluetoothDevicesDiscovery({ services: [6E400001-B5A3-F393-E0A9-E50E24DCCA9E], success: function(res) { console.log(开始搜索设备) } }) } // 连接设备 function connectDevice(deviceId) { wx.createBLEConnection({ deviceId, success: function(res) { console.log(连接成功, res) getServices(deviceId) } }) } // 获取服务 function getServices(deviceId) { wx.getBLEDeviceServices({ deviceId, success: function(res) { const service res.services.find(s s.uuid.startsWith(6E400001)) getCharacteristics(deviceId, service.uuid) } }) } // 获取特征值 function getCharacteristics(deviceId, serviceId) { wx.getBLEDeviceCharacteristics({ deviceId, serviceId, success: function(res) { const txChar res.characteristics.find(c c.uuid.endsWith(03)) const rxChar res.characteristics.find(c c.uuid.endsWith(02)) // 启用通知 wx.notifyBLECharacteristicValueChange({ deviceId, serviceId, characteristicId: txChar.uuid, state: true, }) // 监听数据 wx.onBLECharacteristicValueChange(function(res) { const value ab2hex(res.value) console.log(收到数据:, value) }) // 发送数据示例 function sendData(data) { wx.writeBLECharacteristicValue({ deviceId, serviceId, characteristicId: rxChar.uuid, value: hex2ab(data), }) } } }) } // ArrayBuffer转16进制字符串 function ab2hex(buffer) { return Array.prototype.map.call( new Uint8Array(buffer), x (00 x.toString(16)).slice(-2) ).join() } // 16进制字符串转ArrayBuffer function hex2ab(hex) { let typedArray new Uint8Array(hex.match(/[\da-f]{2}/gi).map(function(h) { return parseInt(h, 16) })) return typedArray.buffer }4. 性能优化与常见问题4.1 通信稳定性提升调整MTU大小默认MTU23字节可能限制数据传输效率。在支持的情况下可以协商更大的MTU# ESP32端无法直接设置MTU但可以响应手机端的MTU请求 _IRQ_MTU_EXCHANGED const(21) def _irq_handler(self, event, data): if event _IRQ_MTU_EXCHANGED: conn_handle, mtu data print(MTU updated:, mtu)数据分包处理当发送超过MTU大小的数据时需要手动分包def send_large_data(self, data, chunk_size20): for i in range(0, len(data), chunk_size): chunk data[i:ichunk_size] self.send(chunk) utime.sleep_ms(10) # 适当延迟防止丢包4.2 功耗管理ESP32在BLE模式下功耗相对较高可以通过以下方式优化调整广播间隔增加广播间隔减少功耗# 将广播间隔从500ms增加到2s self._advertise(interval_us2000000)深度睡眠模式无连接时进入睡眠if not self.is_connected(): machine.deepsleep(10000) # 睡眠10秒降低CPU频率import machine machine.freq(160000000) # 设置为160MHz4.3 常见错误排查错误现象可能原因解决方案无法扫描到设备BLE未激活/名称太长检查ble.active(True)设备名称≤29字符连接后立即断开服务配置错误确认UUID和特征属性正确数据接收不全MTU限制/未启用通知检查手机端是否启用Notify特性发送数据失败连接中断/特征不可写检查is_connected()和特征属性4.4 进阶功能扩展安全配对增加BLE配对提高安全性_IRQ_ENCRYPTION_UPDATE const(28) def _irq_handler(self, event, data): if event _IRQ_ENCRYPTION_UPDATE: conn_handle, encrypted, authenticated data print(f加密状态: {encrypted}, 认证: {authenticated})多服务支持添加更多服务UUID_BATTERY_UUID bluetooth.UUID(180F) _BATTERY_LEVEL (bluetooth.UUID(2A19), bluetooth.FLAG_READ | bluetooth.FLAG_NOTIFY) _BATTERY_SERVICE (_BATTERY_UUID, (_BATTERY_LEVEL,)) services (_UART_SERVICE, _BATTERY_SERVICE) (self._tx_handle, self._rx_handle, self._batt_handle) self._ble.gatts_register_services(services)广播数据增强添加厂商特定数据def _generate_advertising_payload(self): payload super()._generate_advertising_payload() # 添加厂商数据ESP32的厂商ID是0x02E5 payload struct.pack(BBH, 3, 0xFF, 0x02E5) return payload在实际项目中我发现最影响BLE通信稳定性的因素是周围2.4GHz频段的干扰。建议在WiFi和BLE共存的场景下优先使用WiFi信道1、6、11避免与BLE信道重叠。对于需要频繁通信的应用可以适当降低数据传输频率增加错误检测和重传机制。