项目场景:
使用一下代码可以在ESP32-WROOM正常调用蓝牙
from machine import Pin, PWM
from machine import Timer
from time import sleep_ms
import bluetooth
# 全局变量:存储接收到的蓝牙消息
BLE_MSG = ""
class ESP32_BLE():
def __init__(self, name):
# 初始化PWM控制LED(引脚8)
self.led = PWM(Pin(8))
# 设置PWM频率为1000Hz(修正后的写法)
self.led.freq(1000)
# 创建定时器对象(定时器0)
self.timer1 = Timer(0)
# 存储蓝牙设备名称
self.name = name
# 初始化蓝牙模块
self.ble = bluetooth.BLE()
# 激活蓝牙硬件
self.ble.active(True)
# 设置蓝牙设备名称
self.ble.config(gap_name=name)
# 连接状态标志(新增)
self.connected_flag = False
# 初始化设备为断开状态
self.disconnected()
# 设置蓝牙中断处理函数
self.ble.irq(self.ble_irq)
# 注册GATT服务和特征
self.register()
# 开始蓝牙广播
self.advertiser()
def connected(self):
# 设备连接时:
# 1. 点亮LED(占空比100%)
self.led.duty(1023)
# 2. 停止心跳定时器
self.timer1.deinit()
# 3. 更新连接状态
self.connected_flag = True
def disconnected(self):
# 设备断开时:
# 1. 启动心跳定时器(LED闪烁)
self.timer1.init(period=100, mode=Timer.PERIODIC, callback=lambda t: self.led.duty(500))
# 2. 更新连接状态
self.connected_flag = False
def ble_irq(self, event, data):
global BLE_MSG
# 处理不同蓝牙事件:
if event == 1: # 设备连接事件
self.connected()
elif event == 2: # 设备断开事件
self.advertiser()
self.disconnected()
elif event == 3: # 接收到数据事件
# 读取特征值数据
buffer = self.ble.gatts_read(self.rx)
# 解码并存储消息(去除首尾空格)
BLE_MSG = buffer.decode('UTF-8').strip()
def register(self):
# 定义GATT服务和特征:
service_uuid = '6E400001-B5A3-F393-E0A9-E50E24DCCA9E' # UART服务
reader_uuid = '6E400002-B5A3-F393-E0A9-E50E24DCCA9E' # 可写特征
sender_uuid = '6E400003-B5A3-F393-E0A9-E50E24DCCA9E' # 通知特征
# 配置服务结构(元组嵌套)
services = (
(
bluetooth.UUID(service_uuid),
(
(bluetooth.UUID(sender_uuid), bluetooth.FLAG_NOTIFY), # 通知特征
(bluetooth.UUID(reader_uuid), bluetooth.FLAG_WRITE), # 写入特征
)
),
)
# 注册服务并获取特征对象
((self.tx, self.rx,),) = self.ble.gatts_register_services(services)
def send(self, data):
# 数据发送前检查连接状态:
if self.connected_flag:
try:
# 发送数据(添加换行符并编码为字节)
self.ble.gatts_notify(0, self.tx, (data + '\n').encode('utf-8'))
except OSError as e:
print("发送失败:", e)
else:
print("未连接,跳过发送")
def advertiser(self):
# 构造广播数据:
name = bytes(self.name, 'UTF-8') # 设备名称
# 固定广播头部(通用可发现模式)
adv_data = bytearray(b'\x02\x01\x06')
# 添加完整设备名称字段
adv_data.extend(bytes([len(name) + 1, 0x09])) # 字段长度+类型
adv_data.extend(name) # 设备名称
# 开始广播(间隔100ms)
self.ble.gap_advertise(100, adv_data)
print("广播数据:", adv_data, "\r\n")
if __name__ == "__main__":
# 创建蓝牙对象(设备名"ESP32BLE")
ble = ESP32_BLE("ESP32BLE")
# 初始化LED控制(再次确认引脚)
led = PWM(Pin(8))
while True:
# 连接状态指示灯逻辑
if ble.connected_flag:
ble.send('LED is OFF.') # 周期性发送状态
sleep_ms(1000)
# 处理接收的命令:
if BLE_MSG == 'LED ON':
print("收到命令:", BLE_MSG)
# 渐亮效果
for i in range(0, 1023):
led.duty(i)
sleep_ms(3)
BLE_MSG = ""
print("LED已打开")
ble.send('LED is ON.') # 发送确认信息
elif BLE_MSG == 'LED OFF':
print("收到命令:", BLE_MSG)
# 渐灭效果
for i in range(1023, -1, -1):
led.duty(i)
sleep_ms(3)
BLE_MSG = ""
print("LED已关闭")
ble.send('LED is OFF.')
sleep_ms(100) # 主循环延时
问题描述
但是在ESP32 C3mini平台上提示
错误代码128
原因分析:
大佬回复:
128报错是通信报错,因为蓝牙是虚拟串口的形式发送信息,所以当你的self.tx是0的时候,它实际上使用的是uart0,也就是esp32S3的repl口,这个口肯定是没法用的,所以你需要把self.tx改成1,这样他就会使用uart1,就和原本的uart0不冲突了
我不知道这个对不对,因为tx口是由系统自己分配的。
下面是我的分析,结合AI:
更改以下函数,esp32 mini即可正常使用
def send(self, data):
# 数据发送前检查连接状态:
if self.connected_flag:
try:
# 发送数据(添加换行符并编码为字节)
self.ble.gatts_notify(1, self.tx, (data + '\n').encode('utf-8'))
except OSError as e:
print("发送失败:", e)
else:
print("未连接,跳过发送")
为什么 WROOM 能工作而 C3 失败
1. 连接句柄的本质
-
连接句柄(Connection Handle) 是 BLE 协议栈为每个活跃连接分配的唯一标识符(类似 TCP 的端口号)
-
取值范围:
0x0000
~0xFFFF
(实际使用中通常从 0 或 1 开始) -
0xFFFF
是保留值,表示无效连接
2. 不同芯片的分配策略
芯片型号 | 协议栈实现 | 首连接句柄 | 多连接支持 |
---|---|---|---|
ESP32-WROOM | NimBLE (传统) | 0 | 否 |
ESP32-C3 | Bluedroid (新版) | 1 | 是 |
为什么参数为 1 可以,0 不行
1. 当使用 gatts_notify(1, ...)
-
ESP32-C3:正确匹配首个连接的句柄 1(Bluedroid 协议栈特性)
-
ESP32-WROOM:实际会忽略无效句柄 1,但:
-
旧协议栈自动回退到唯一连接句柄 0
-
表现出 "错误参数但能工作" 的假象
-
2. 当使用 gatts_notify(0, ...)
-
ESP32-C3:0 是无效连接句柄 → 触发
OSError: 128
-
ESP32-WROOM:0 是有效句柄 → 正常工作