webbluetooth蓝牙设备服务枚举不全问题解决

77 阅读3分钟

electron开发的客户端中想操作蓝牙设备,找到第三方类库nodejs的类库webbluetooth 。在mac系统上ok,在window系统上出现蓝牙设备的服务枚举不全,通过增加输出日志的方式定位到代码。并记录解决方法。

SimpleBLE\simpleble\src\frontends\base\Peripheral.cpp

  • 获取服务,当设备连接连接成功后is_connected()为true,调用services获取到服务列表。
  • 但是
std::vector<Service> Peripheral::services() {
    if (!initialized()) throw Exception::NotInitialized();

    if (is_connected()) {
        return internal_->services();
    } else {
        return internal_->advertised_services();
    }

    return internal_->services();
}

SimpleBLE\simpleble\src\backends\windows\PeripheralBase.cpp

  • 服务枚举中用到了gatt_map_缓存数据,那这个数据是在什么时候填充进去的呢?
  • 通过查找gatt_map_发现是在设备连接的方法中填充进去的。
std::vector<Service> PeripheralBase::services() {
    std::vector<Service> service_list;
    for (auto& [service_uuid, service] : gatt_map_) {
        // Build the list of characteristics for the service.
        std::vector<Characteristic> characteristic_list;
        for (auto& [characteristic_uuid, characteristic] : service.characteristics) {
            // Build the list of descriptors for the characteristic.
            std::vector<Descriptor> descriptor_list;
            for (auto& [descriptor_uuid, descriptor] : characteristic.descriptors) {
                descriptor_list.push_back(DescriptorBuilder(descriptor_uuid));
            }

            uint32_t properties = (uint32_t)characteristic.obj.CharacteristicProperties();
            bool can_read = (properties & (uint32_t)GattCharacteristicProperties::Read) != 0;
            bool can_write_request = (properties & (uint32_t)GattCharacteristicProperties::Write) != 0;
            bool can_write_command = (properties & (uint32_t)GattCharacteristicProperties::WriteWithoutResponse) != 0;
            bool can_notify = (properties & (uint32_t)GattCharacteristicProperties::Notify) != 0;
            bool can_indicate = (properties & (uint32_t)GattCharacteristicProperties::Indicate) != 0;

            characteristic_list.push_back(CharacteristicBuilder(characteristic_uuid, descriptor_list, can_read,
                                                                can_write_request, can_write_command, can_notify,
                                                                can_indicate));
        }
        service_list.push_back(ServiceBuilder(service_uuid, characteristic_list));
    }

    return service_list;
}

SimpleBLE\simpleble\src\backends\windows\PeripheralBase.cpp

  • 设备连接方法connect中调用了_attempt_connect,就是用来枚举服务、特征值、描述符的。
  • _attempt_connect中第一层for循环,若特征值枚举失败,就直接返回falsele .若该服务下确实没有特征值,则会出现服务枚举不全的情况。比如本来8个服务只枚举出来6个。
  • 改造for,使用continue代替false.问题解决。以下是调测的代码, 供参考、备忘。
void PeripheralBase::connect() {
    device_ = async_get(BluetoothLEDevice::FromBluetoothAddressAsync(_str_to_mac_address(address_)));

    // Attempt to connect to the device.
    for (size_t i = 0; i < 3; i++) {
        if (_attempt_connect()) {
            break;
        }
    }

    if (is_connected()) {
        connection_status_changed_token_ = device_.ConnectionStatusChanged(
            [this](const BluetoothLEDevice device, const auto args) {
                if (device.ConnectionStatus() == BluetoothConnectionStatus::Disconnected) {
                    this->disconnection_cv_.notify_all();

                    SAFE_CALLBACK_CALL(this->callback_on_disconnected_);
                }
            });

        SAFE_CALLBACK_CALL(this->callback_on_connected_);
    }
}

bool PeripheralBase::_attempt_connect() {
    gatt_map_.clear();
    std::cout << "ssss gatt_map_ PeripheralBase::_attempt_connect 执行了 : "  << std::endl;

    // We need to cache all services, characteristics and descriptors in the class, else
    // the underlying objects will be garbage collected.
    auto services_result = async_get(device_.GetGattServicesAsync(BluetoothCacheMode::Uncached));
    if (services_result.Status() != GattCommunicationStatus::Success) {
        std::cout << "ssss gatt_map_ PeripheralBase::_attempt_connect 准备退出false : "  << std::endl;
        return false;
    }
    int testIndex = 0;// for test

    auto gatt_services = services_result.Services();
    std::cout << "ssss gatt_map_  PeripheralBase::_attempt_connect services_result.Services()执行了 "  << std::endl;
    for (GattDeviceService&& service : gatt_services) {
        testIndex++;
        // For each service...
        gatt_service_t gatt_service;
        gatt_service.obj = service;

        // Save the MTU size
        mtu_ = service.Session().MaxPduSize();

        // Fetch the service UUID
        std::string service_uuid = guid_to_uuid(service.Uuid());

        // Fetch the service characteristics
        auto characteristics_result = async_get(service.GetCharacteristicsAsync(BluetoothCacheMode::Uncached));
        if (characteristics_result.Status() != GattCommunicationStatus::Success) {
            std::cout << "ssss gatt_map_ PeripheralBase::_attempt_connect 准备退出false : 特征值出错 service_uuid= " << service_uuid << std::endl;
            gatt_map_.emplace(service_uuid, std::move(gatt_service));
            continue;
            // return false;
        }

        // Load the characteristics into the service
        auto gatt_characteristics = characteristics_result.Characteristics();
        for (GattCharacteristic&& characteristic : gatt_characteristics) {
            // For each characteristic...
            gatt_characteristic_t gatt_characteristic;
            gatt_characteristic.obj = characteristic;

            // Fetch the characteristic UUID
            std::string characteristic_uuid = guid_to_uuid(characteristic.Uuid());

            // Fetch the characteristic descriptors
            auto descriptors_result = async_get(characteristic.GetDescriptorsAsync(BluetoothCacheMode::Uncached));
            if (descriptors_result.Status() != GattCommunicationStatus::Success) {
                std::cout << "ssss gatt_map_ PeripheralBase::_attempt_connect 准备退出false : 描述符出错 "  << std::endl;
                gatt_map_.emplace(service_uuid, std::move(gatt_service));
                continue;
            // return false;
            }

            // Load the descriptors into the characteristic
            auto gatt_descriptors = descriptors_result.Descriptors();
            for (GattDescriptor&& descriptor : gatt_descriptors) {
                // For each descriptor...
                gatt_descriptor_t gatt_descriptor;
                gatt_descriptor.obj = descriptor;

                // Fetch the descriptor UUID.
                std::string descriptor_uuid = guid_to_uuid(descriptor.Uuid());

                // Append the descriptor to the characteristic.
                gatt_characteristic.descriptors.emplace(descriptor_uuid, std::move(gatt_descriptor));
            }

            // Append the characteristic to the service.
            gatt_service.characteristics.emplace(characteristic_uuid, std::move(gatt_characteristic));
        }

        // Append the service to the map.
        gatt_map_.emplace(service_uuid, std::move(gatt_service));
    }
    std::cout << "ssss gatt_map_.emplace testIndex : " << testIndex << std::endl;
    std::cout << "ssss gatt_map_  PeripheralBase::_attempt_connect 准备退出 true "  << std::endl;
    return true;
}