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

树莓派Zero W蓝牙扫描代码异常求助:C无结果Python报错

树莓派Zero W蓝牙扫描程序故障排查

问题描述

  • 采用网上的C示例代码编写BLE扫描程序,无编译/运行报错,但始终无法发现设备,num_rsp变量持续为0
  • 执行hcitool lescan可正常列出周边BLE设备,证明蓝牙硬件及驱动功能正常
  • 使用Python Bleak库编写扫描脚本时,触发org.bluez.Error.InProgress错误,提示操作已在进行中
  • 已将bluez更新至最新版本,bluetoothctl和hcitool等工具可正常工作,但自行编写的代码均无法正常完成扫描

Bleak报错代码及错误信息

Python代码:

import sys
import platform
import logging
import binascii
import asyncio
from bleak import BleakScanner

async def main():
    async with BleakScanner() as scanner:
        devices = await scanner.discover()
        for d in devices:
            print(d)

asyncio.run(main())

错误日志:

Traceback (most recent call last):
  File "/home/pi/stryd.py", line 16, in <module>
    asyncio.run(main())
  File "/usr/lib/python3.9/asyncio/runners.py", line 44, in run
    return loop.run_until_complete(main)
  File "/usr/lib/python3.9/asyncio/base_events.py", line 642, in run_until_complete
    return future.result()
  File "/home/pi/stryd.py", line 11, in main
    devices = await scanner.discover()
  File "/usr/local/lib/python3.9/dist-packages/bleak/__init__.py", line 216, in discover
    async with cls(**kwargs) as scanner:
  File "/usr/local/lib/python3.9/dist-packages/bleak/__init__.py", line 126, in __aenter__
    await self._backend.start()
  File "/usr/local/lib/python3.9/dist-packages/bleak/backends/bluezdbus/scanner.py", line 191, in start
    self._stop = await manager.active_scan(
  File "/usr/local/lib/python3.9/dist-packages/bleak/backends/bluezdbus/manager.py", line 368, in active_scan
    assert_reply(reply)
  File "/usr/local/lib/python3.9/dist-packages/bleak/backends/bluezdbus/utils.py", line 20, in assert_reply
    raise BleakDBusError(reply.error_name, reply.body)
bleak.exc.BleakDBusError: [org.bluez.Error.InProgress] Operation already in progress

原始C示例代码

#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
#include <curses.h>
#include <sys/ioctl.h>

#include <sys/socket.h>
#include <bluetooth/bluetooth.h>
#include <bluetooth/hci.h>
#include <bluetooth/hci_lib.h>

#define HCI_STATE_NONE       0
#define HCI_STATE_OPEN       2
#define HCI_STATE_SCANNING   3
#define HCI_STATE_FILTERING  4

#define EIR_FLAGS                   0X01
#define EIR_NAME_SHORT              0x08
#define EIR_NAME_COMPLETE           0x09
#define EIR_MANUFACTURE_SPECIFIC    0xFF

struct hci_state {
  int device_id;
  int device_handle;
  struct hci_filter original_filter;
  int state;
  int has_error;
  char error_message[1024];
} hci_state;


int main(int argc, char **argv)
{
    struct hci_state dev_id;
    inquiry_info *ii = NULL;
    int max_rsp, num_rsp;
    int sock, len, flags;
    int i;
    char addr[19] = { 0 };
    char name[248] = { 0 };

    dev_id.device_id = hci_get_route(NULL);

    if((dev_id.device_handle = hci_open_dev(dev_id.device_id)) < 0){
        dev_id.has_error = TRUE;
        snprintf(dev_id.error_message, sizeof(dev_id.error_message), "Could not open device: %s", strerror(errno));
    }

   // Set fd non-blocking
   int on = 1;
   if(ioctl(dev_id.device_handle, FIONBIO, (char *)&on) < 0){
        dev_id.has_error = TRUE;
        snprintf(dev_id.error_message, sizeof(dev_id.error_message), "Could set device to non-blocking: %s", strerror(errno));
    }

    dev_id.state = HCI_STATE_OPEN;

    if (hci_le_set_scan_parameters(dev_id.device_handle, 0x01, htobs(0x0010), htobs(0x0010), 0x00, 0x00, 1000) < 0){
        dev_id.has_error = 1;
        snprintf(dev_id.error_message, sizeof(dev_id.error_message), "Failed to set scan parameters: %s", strerror(errno));
    }

    if (hci_le_set_scan_enable(dev_id.device_handle, 0x01, 1, 1000) < 0){
        dev_id.has_error = 1;
        snprintf(dev_id.error_message, sizeof(dev_id.error_message), "Failed to enable scan: %s", strerror(errno));
    }
    sock = dev_id.device_handle;

    len  = 8;
    max_rsp = 255;
    flags = IREQ_CACHE_FLUSH;
    ii = (inquiry_info*)malloc(max_rsp * sizeof(inquiry_info));
    
    num_rsp = hci_inquiry(dev_id.device_id, len, max_rsp, NULL, &ii, flags);
    if( num_rsp < 0 ) perror("hci_inquiry");

    for (i = 0; i < num_rsp; i++) {
        ba2str(&(ii+i)->bdaddr, addr);
        memset(name, 0, sizeof(name));
        if (hci_read_remote_name(sock, &(ii+i)->bdaddr, sizeof(name), 
            name, 0) < 0)
        strcpy(name, "[unknown]");
        printf("%s  %s\n", addr, name);
    }

    free( ii );
    close( sock );
    return 0;
}

解决方案

1. Bleak库报错修复

原因:系统中已有蓝牙扫描进程在运行(比如bluetoothctl扫描未停止、hcitool lescan后台进程未结束),导致BlueZ拒绝新的扫描请求。

修复步骤:

  • 先停止所有正在运行的蓝牙扫描进程:
    sudo bluetoothctl scan off
    killall hcitool
    
  • 或修改Python代码,明确指定扫描超时时间,简化扫描逻辑:
    import asyncio
    from bleak import BleakScanner
    
    async def main():
        # 指定扫描超时为5秒
        devices = await BleakScanner.discover(timeout=5)
        for d in devices:
            print(d)
    
    asyncio.run(main())
    

2. C代码扫描BLE设备修复

原因:原始代码混用了经典蓝牙(BR/EDR)扫描API (hci_inquiry) 和 BLE扫描配置,hci_inquiry仅能扫描经典蓝牙设备,无法获取BLE广告包,因此num_rsp始终为0。

修正后的C代码:

#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <bluetooth/bluetooth.h>
#include <bluetooth/hci.h>
#include <bluetooth/hci_lib.h>

#define SCAN_DURATION 5  // 扫描时长(秒)

// 解析BLE广告数据中的设备名称
void parse_eir_data(uint8_t *eir, char *name, size_t name_len) {
    uint8_t *ptr = eir;
    while (*ptr) {
        uint8_t len = *ptr++;
        uint8_t type = *ptr++;
        if (type == 0x09 || type == 0x08) {  // 完整名称/短名称
            snprintf(name, name_len, "%.*s", len - 1, ptr);
            break;
        }
        ptr += len - 1;
    }
}

int main() {
    int dev_id = hci_get_route(NULL);
    int sock = hci_open_dev(dev_id);
    if (sock < 0) {
        perror("hci_open_dev");
        exit(EXIT_FAILURE);
    }

    // 设置BLE扫描参数:主动扫描,间隔10ms,窗口10ms
    le_set_scan_parameters_cp scan_params = {
        .type = 0x01,  // 主动扫描
        .interval = htobs(0x0010),
        .window = htobs(0x0010),
        .own_bdaddr_type = 0x00,
        .filter_policy = 0x00
    };
    if (hci_le_set_scan_parameters(sock, &scan_params, 1000) < 0) {
        perror("hci_le_set_scan_parameters");
        close(sock);
        exit(EXIT_FAILURE);
    }

    // 开启BLE扫描
    if (hci_le_set_scan_enable(sock, 0x01, 1, 1000) < 0) {
        perror("hci_le_set_scan_enable");
        close(sock);
        exit(EXIT_FAILURE);
    }

    // 设置HCI过滤,只接收BLE广告事件
    struct hci_filter flt;
    hci_filter_clear(&flt);
    hci_filter_set_ptype(HCI_EVENT_PKT, &flt);
    hci_filter_set_event(EVT_LE_META_EVENT, &flt);
    if (setsockopt(sock, SOL_HCI, HCI_FILTER, &flt, sizeof(flt)) < 0) {
        perror("setsockopt HCI_FILTER");
        close(sock);
        exit(EXIT_FAILURE);
    }

    printf("扫描BLE设备中,持续%d秒...\n", SCAN_DURATION);
    fd_set read_set;
    struct timeval timeout;
    timeout.tv_sec = SCAN_DURATION;
    timeout.tv_usec = 0;

    while (1) {
        FD_ZERO(&read_set);
        FD_SET(sock, &read_set);

        int ret = select(sock + 1, &read_set, NULL, NULL, &timeout);
        if (ret == 0) {  // 超时结束扫描
            break;
        } else if (ret < 0) {
            perror("select");
            break;
        }

        if (FD_ISSET(sock, &read_set)) {
            uint8_t buf[HCI_MAX_EVENT_SIZE];
            int len = read(sock, buf, sizeof(buf));
            if (len < 0) {
                perror("read");
                continue;
            }

            // 解析BLE广告事件
            evt_le_meta_event *meta = (evt_le_meta_event *)(buf + 2);
            if (meta->subevent != EVT_LE_ADVERTISING_REPORT) {
                continue;
            }

            le_advertising_info *info = (le_advertising_info *)(meta->data + 1);
            char addr[19] = {0};
            char name[256] = "[unknown]";
            ba2str(&info->bdaddr, addr);
            parse_eir_data(info->data, name, sizeof(name));
            printf("设备地址: %s  名称: %s  RSSI: %d\n", addr, name, info->data[info->length]);
        }
    }

    // 关闭扫描
    hci_le_set_scan_enable(sock, 0x00, 1, 1000);
    close(sock);
    return 0;
}

编译命令:

gcc ble_scan.c -o ble_scan -lbluetooth

说明:

  • 改用hci_le_set_scan_parameters和hci_le_set_scan_enable配置BLE扫描
  • 通过select监听HCI套接字,读取BLE广告事件(EVT_LE_META_EVENT)
  • 解析广告数据中的设备名称和RSSI信息
  • 扫描结束后主动关闭BLE扫描

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.01 23:10:43