En esta página

Escáner Bluetooth de baja energía

Una aplicación diseñada para explorar el contenido de dispositivos periféricos Bluetooth Low Energy. El ejemplo demuestra el uso de todas las clases Qt Bluetooth clases Low Energy.

El ejemplo de escáner Bluetooth Low Energy muestra cómo desarrollar aplicaciones Bluetooth Low Energy utilizando la clase Qt Bluetooth API. La aplicación cubre el escaneo de dispositivos Low Energy, el escaneo de sus servicios y la lectura de las características y descriptores del servicio.

Servicios GATT encontrados mediante el escáner Qt BLE

El ejemplo introduce las siguientes clases Qt:

El ejemplo se puede utilizar con cualquier dispositivo periférico Bluetooth Low Energy arbitrario. Crea una instantánea de todos los servicios, características y descriptores y los presenta al usuario. Por lo tanto, la aplicación proporciona una manera fácil de navegar por el contenido ofrecido por un dispositivo periférico.

Ejecución del ejemplo

Para ejecutar el ejemplo desde Qt Creator, abra el modo Welcome y seleccione el ejemplo de Examples. Para más información, consulte Qt Creator: Tutorial: Construir y ejecutar.

Solicitar permiso para utilizar Bluetooth

En determinadas plataformas, es necesario conceder explícitamente permisos para utilizar Bluetooth. El ejemplo utiliza el objeto QML BluetoothPermission para comprobar y solicitar los permisos, si es necesario:

BluetoothPermission {
    id: permission
    communicationModes: BluetoothPermission.Access
    onStatusChanged: {
        if (permission.status === Qt.PermissionStatus.Denied)
            Device.update = "Bluetooth permission required"
        else if (permission.status === Qt.PermissionStatus.Granted)
            devicesPage.toggleDiscovery()
    }
}

El cuadro de diálogo de solicitud de permisos se activa cuando el usuario intenta iniciar la detección de dispositivos, y el estado del permiso es Undetermined:

onButtonClick: {
    if (permission.status === Qt.PermissionStatus.Undetermined)
        permission.request()
    else if (permission.status === Qt.PermissionStatus.Granted)
        devicesPage.toggleDiscovery()
}

El descubrimiento del dispositivo se inicia si el permiso es concedido por el usuario. En caso contrario, la aplicación no funciona.

Búsqueda de dispositivos

El primer paso es encontrar todos los dispositivos periféricos. Los dispositivos pueden ser encontrados usando la clase QBluetoothDeviceDiscoveryAgent. El proceso de descubrimiento se inicia utilizando start(). Cada nuevo dispositivo se anuncia a través de la señal deviceDiscovered():

discoveryAgent = new QBluetoothDeviceDiscoveryAgent(this);
discoveryAgent->setLowEnergyDiscoveryTimeout(25000);
connect(discoveryAgent, &QBluetoothDeviceDiscoveryAgent::deviceDiscovered,
        this, &Device::addDevice);
connect(discoveryAgent, &QBluetoothDeviceDiscoveryAgent::errorOccurred,
        this, &Device::deviceScanError);
connect(discoveryAgent, &QBluetoothDeviceDiscoveryAgent::finished,
        this, &Device::deviceScanFinished);
connect(discoveryAgent, &QBluetoothDeviceDiscoveryAgent::canceled,
        this, &Device::deviceScanFinished);
discoveryAgent->start(QBluetoothDeviceDiscoveryAgent::LowEnergyMethod);

La siguiente ranura addDevice() se activa como reacción al descubrimiento de un nuevo dispositivo. Filtra todos los dispositivos encontrados que tienen la bandera QBluetoothDeviceInfo::LowEnergyCoreConfiguration y los añade a una lista que se muestra al usuario. La señal deviceDiscovered() puede emitirse varias veces para el mismo dispositivo a medida que se descubren más detalles. Aquí emparejamos estos descubrimientos de dispositivos para que el usuario sólo vea los dispositivos individuales:

void Device::addDevice(const QBluetoothDeviceInfo &info)
{
    if (info.coreConfigurations() & QBluetoothDeviceInfo::LowEnergyCoreConfiguration) {
        auto devInfo = new DeviceInfo(info);
        auto it = std::find_if(devices.begin(), devices.end(),
                               [devInfo](DeviceInfo *dev) {
                                   return devInfo->getAddress() == dev->getAddress();
                               });
        if (it == devices.end()) {
            devices.append(devInfo);
        } else {
            auto oldDev = *it;
            *it = devInfo;
            delete oldDev;
        }
        emit devicesUpdated();
    }
}

La lista de dispositivos puede parecerse a la de la imagen siguiente.

Nota: Es un requisito previo que los dispositivos remotos anuncien activamente su presencia.

Dispositivos cercanos listados en el escáner Qt BLE

Conexión a servicios

Una vez que el usuario ha seleccionado un dispositivo de la lista, la aplicación se conecta al dispositivo y explora todos los servicios. La clase QLowEnergyController se utiliza para conectarse al dispositivo. La función QLowEnergyController::connectToDevice() desencadena el proceso de conexión, que dura hasta que se recibe la señal QLowEnergyController::connected() o se produce un error:

if (!controller) {
    // Connecting signals and slots for connecting to LE services.
    controller = QLowEnergyController::createCentral(currentDevice.getDevice(), this);
    connect(controller, &QLowEnergyController::connected,
            this, &Device::deviceConnected);
    connect(controller, &QLowEnergyController::errorOccurred, this, &Device::errorReceived);
    connect(controller, &QLowEnergyController::disconnected,
            this, &Device::deviceDisconnected);
    connect(controller, &QLowEnergyController::serviceDiscovered,
            this, &Device::addLowEnergyService);
    connect(controller, &QLowEnergyController::discoveryFinished,
            this, &Device::serviceScanDone);
}

if (isRandomAddress())
    controller->setRemoteAddressType(QLowEnergyController::RandomAddress);
else
    controller->setRemoteAddressType(QLowEnergyController::PublicAddress);
controller->connectToDevice();

La ranura activada por la señal connected() llama inmediatamente a QLowEnergyController::discoverServices() para iniciar el descubrimiento de servicios en el dispositivo periférico conectado.

controller->discoverServices();

La siguiente imagen muestra los resultados cuando se selecciona el dispositivo SensorTag. La vista muestra los nombres de los servicios, si son servicios primarios o secundarios y el UUID que determina el tipo de servicio.

Servicios GATT encontrados mediante el escáner Qt BLE

En cuanto se selecciona el servicio, se crea la instancia QLowEnergyService correspondiente para poder interactuar con él:

QLowEnergyService *service =  controller->createServiceObject(serviceUuid);if (!service) {    qWarning() << "Cannot create service for uuid";
   return; }

El objeto de servicio proporciona las señales y funciones necesarias para descubrir los detalles del servicio, leer y escribir características y descriptores, así como recibir notificaciones de cambio de datos. Las notificaciones de cambio pueden activarse como resultado de la escritura de un valor o debido a una actualización en el dispositivo activada potencialmente por la lógica interna. Durante la búsqueda inicial de detalles, el servicio state() pasa de RemoteService a RemoteServiceDiscovering y finalmente termina con RemoteServiceDiscovered:

connect(service, &QLowEnergyService::stateChanged,
        this, &Device::serviceDetailsDiscovered);
service->discoverDetails();
setUpdate(u"Back\n(Discovering details...)"_s);

Lectura de datos del servicio

Al seleccionar un servicio, se muestran los detalles del mismo. Cada característica aparece junto con su nombre, UUID, valor, manejador y propiedades.

Características de GATT en Qt BLE Scanner

Es posible recuperar las características del servicio a través de QLowEnergyService::characteristics() y, a su vez, cada descriptor puede obtenerse a través de QLowEnergyCharacteristic::descriptors().

const QList<QLowEnergyCharacteristic> chars = service->characteristics();
for (const QLowEnergyCharacteristic &ch : chars) {
    auto cInfo = new CharacteristicInfo(ch);
    m_characteristics.append(cInfo);
}

Aunque la aplicación de ejemplo no muestra los descriptores, los utiliza para obtener el nombre de una característica individual si no se puede discernir su nombre basándose en su UUID. La segunda forma de obtener el nombre es la existencia de un descriptor del tipo QBluetoothUuid::DescriptorType::CharacteristicUserDescription. El código que figura a continuación muestra cómo conseguirlo:

QString name = m_characteristic.name();
if (!name.isEmpty())
    return name;

// find descriptor with CharacteristicUserDescription
const QList<QLowEnergyDescriptor> descriptors = m_characteristic.descriptors();
for (const QLowEnergyDescriptor &descriptor : descriptors) {
    if (descriptor.type() == QBluetoothUuid::DescriptorType::CharacteristicUserDescription) {
        name = descriptor.value();
        break;
    }
}

Proyecto de ejemplo @ code.qt.io

© 2026 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.