You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何停止asyncio循环:使用bleak读Arduino BLE数据时Ctrl+C失效问题

问题原因及修复方案

核心问题点

  • 你在asyncio协程中使用了同步阻塞的time.sleep(),会卡住整个事件循环,导致操作系统的Ctrl+C中断信号(SIGINT)无法被事件循环及时处理,这是Ctrl+C失效的核心诱因
  • shutdown()是普通同步函数,但client.disconnect()是bleak提供的异步方法,你同步调用该方法根本无法正常执行断开逻辑,且用loop.run_until_complete()运行同步函数本身就是错误用法
  • main函数是异步协程,你在if __name__ == "__main__"中直接调用main()属于语法错误,该代码块和上方全局的事件循环执行逻辑也存在冲突
  • 你全程没有启动GATT特征的notify监听,代码末尾的await client.stop_notify()属于无效调用
  • main函数中捕获了所有Exception类型异常,会拦截部分运行时错误导致外层无法感知

修复后可直接运行的代码

import asyncio
import struct
import numpy as np
from datetime import datetime
from bleak import BleakClient

# 修复了原变量名的拼写错误 LENGHT -> LENGTH
BUFFER_LENGTH = 13
PACKET_NUMBER = BUFFER_LENGTH * 2
address = "04:56:14:27:55:E8"
MODEL_NBR_UUID = "0000101a-0000-1000-8000-00805f9b34fb"

client = None  # 提前声明全局变量避免引用报错

def process_data(dati):
    data = np.array(struct.unpack('H' * BUFFER_LENGTH, dati))
    print('_____________DATA_____________')
    print(data)
    print('_____________END______________')

# 改为异步函数,适配bleak的异步断开逻辑
async def shutdown():
    global client
    if client and client.is_connected:
        await client.disconnect()
    print('_____________INTERRUPTED_____________')

async def main(address):
    global client
    client = BleakClient(address)
    # 优化重连逻辑,避免连接失败疯狂打日志
    while not client.is_connected:
        try:
            await client.connect()
            print("蓝牙设备连接成功")
        except Exception as e:
            print(f"连接失败,1秒后重试: {e}")
            await asyncio.sleep(1)

    try:
        # 替换同步sleep为异步sleep,不阻塞事件循环
        await asyncio.sleep(1)
        start_timestamp = datetime.timestamp(datetime.now())
        while True:
            dati = await client.read_gatt_char(MODEL_NBR_UUID)
            process_data(dati)
            await asyncio.sleep(2)
    # 仅捕获业务异常,不拦截中断信号
    except Exception as e:
        print(f"运行出错: {e}")
    finally:
        # 无论正常退出还是异常退出都主动断开连接
        if client.is_connected:
            await client.disconnect()

if __name__ == "__main__":
    loop = asyncio.get_event_loop()
    try:
        loop.run_until_complete(main(address))
    except KeyboardInterrupt:
        print("Process interrupted")
        loop.run_until_complete(shutdown())
    finally:
        loop.close()

额外优化建议

  • 所有异步开发场景下都不要调用同步阻塞方法,优先使用对应库的异步实现,避免卡住事件循环
  • 如果后续需要更高的实时性,可以把主动轮询read_gatt_char的逻辑改成bleak的notify回调模式,不需要主动定时读,设备有数据更新会主动推送给PC
  • 尽量减少全局变量的使用,这里为了兼容你原有的写法保留了client全局变量,更规范的写法可以把client作为参数传递或者封装到类中

内容的提问来源于stack exchange,提问作者valio

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.26 16:54:04