当前位置: 首页 > news >正文

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 Service(NUS)UUID,这是蓝牙串口协议的通用实现:

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, name="ESP32-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_us=500000): payload = self._generate_advertising_payload() self._ble.gap_advertise(interval_us, adv_data=payload) 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) > 0

3. 实现双向通信

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(b"Echo: " + data) ble.on_receive(on_data_received) counter = 0 while True: if ble.is_connected(): # 每隔1秒发送一次计数 ble.send(f"Count: {counter}".encode()) counter += 1 utime.sleep(1)

3.2 手机端连接与测试

对于Android用户,推荐使用"nRF Connect"或"BLE Scanner"等应用;iOS用户可以使用"LightBlue"。连接步骤如下:

  1. 打开BLE扫描工具,搜索名为"ESP32-UART"的设备
  2. 连接设备后,查找UUID为"6E400001-B5A3-F393-E0A9-E50E24DCCA9E"的服务
  3. 启用通知(Notify)特性(UUID以...03结尾)
  4. 向写入特性(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大小:默认MTU(23字节)可能限制数据传输效率。在支持的情况下,可以协商更大的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_size=20): for i in range(0, len(data), chunk_size): chunk = data[i:i+chunk_size] self.send(chunk) utime.sleep_ms(10) # 适当延迟防止丢包

4.2 功耗管理

ESP32在BLE模式下功耗相对较高,可以通过以下方式优化:

  1. 调整广播间隔:增加广播间隔减少功耗

    # 将广播间隔从500ms增加到2s self._advertise(interval_us=2000000)
  2. 深度睡眠模式:无连接时进入睡眠

    if not self.is_connected(): machine.deepsleep(10000) # 睡眠10秒
  3. 降低CPU频率

    import machine machine.freq(160000000) # 设置为160MHz

4.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信道重叠。对于需要频繁通信的应用,可以适当降低数据传输频率,增加错误检测和重传机制。

http://www.jsqmd.com/news/484078/

相关文章:

  • 钓鱼即服务产业化演进与企业防御体系重构研究
  • 用R语言进行土壤侵蚀数据分析
  • 用MATLAB boxplot函数做科研数据分析:箱线图实战案例解析
  • 中兴交换机配置加固方法
  • 【C++】string类--常见接口及其模拟实现
  • 最新!2026年OpenClaw(Clawdbot)云端5分钟集成及使用方法
  • 发光二极管(LED)介绍
  • 解决Notepad++绿色版右键菜单失效的3种方法(注册表+bat+权限问题排查)
  • 探索基于出行链的电动汽车负荷预测模型
  • 2026年热销榜单:十大动环监控系统推荐,让你的机房管理更高效
  • 【MySql】navicat连接报2013错误
  • 低查重不是梦!AI教材写作工具,助力快速且高质量完成教材编写!
  • Go 语言实现 Function Calling 服务端:从协议解析到工具执行
  • 【FFmpeg】H.264 格式分析 ② ( 网络抽象层单元 NALU NALU 功能结构 VCL 视频编码层 NAL 网络提取层 H.264 封装模式 - annexb 模式 )
  • C++ 模板编程的实战应用
  • HCIP-AI-EI Developer V2.5 第二章笔记
  • 剪映专业版教程:制作扇形开合效果
  • JavaScript性能优化实战宗弊
  • 【Flask】四、flask连接并操作数据库
  • crontab 定时任务从入门到上线(语法 + 排障)
  • 基于RRT的路径规划算法在多种移动设备上的实现
  • 探索MATLAB中多个无人船协同围捕控制算法
  • 探索 BP 神经网络 PID 控制在 Simulink 中的仿真之旅
  • JavaScript性能优化实战烂文
  • 贾子认知理论与全球主流AI大模型十四项核心弊端:诊断与根治方案
  • Linux 安装 MySQL 与远程连接排障(yum 方案)
  • Scholar-Agent:你的全自动文献调研工具
  • VF控制的仿真与代码生成
  • 拒绝Python依赖!SpringBoot 3 + ONNX Runtime 打造纯Java版YOLOv8通用检测服务:从模型转换到高并发API封装的全链路实战
  • 虚拟机-持续部署流水线最简工具yunedit-ssh