En esta página

Servidor de frecuencia cardiaca Bluetooth de baja energía

Un ejemplo que demuestra cómo configurar y anunciar un servicio GATT. El ejemplo demuestra el uso de las clases Qt Bluetooth Low Energy relacionadas con la funcionalidad periférica (esclava).

El Servidor de Ritmo Cardíaco Bluetooth Low Energy es una aplicación de línea de comandos que muestra cómo desarrollar un servidor Bluetooth GATT utilizando la clase Qt Bluetooth API. La aplicación cubre la configuración de un servicio, su publicidad y la notificación a los clientes de los cambios en los valores de las características.

El ejemplo hace uso de las siguientes clases Qt:

El ejemplo implementa una aplicación de servidor, lo que significa que no tiene interfaz gráfica de usuario. Para visualizar lo que está haciendo, puede utilizar el ejemplo del Juego del Ritmo Cardíaco, que es básicamente la contraparte del lado del cliente de esta aplicación.

Comprobación de permisos Bluetooth

Antes de que la aplicación pueda crear un servicio y empezar a anunciarse, tenemos que comprobar si la aplicación tiene permiso para usar Bluetooth.

auto permissionStatus = app.checkPermission(QBluetoothPermission{});

Solicitar permiso Bluetooth

Si el estado de autorización de Bluetooth es indeterminado, tenemos que solicitar un permiso para utilizar Bluetooth.

if (permissionStatus == Qt::PermissionStatus::Indeterminado) {    qInfo("Requesting Bluetooth permission ...");
    app.requestPermission(QBluetoothPermission{}, [&permissionStatus](const QPermission &permission){        qApp->exit();
        permissionStatus = permission.status(); }); // Ahora, espere a que se resuelva la solicitud de permiso.app.exec(); }

Configuración de datos y parámetros publicitarios

Se utilizan dos clases para configurar el proceso de publicidad:

En nuestro ejemplo, simplemente utilizamos los parámetros por defecto.

La información contenida en QLowEnergyAdvertisingData será visible para otros dispositivos que estén escaneando en ese momento. Pueden utilizarla para decidir si quieren establecer una conexión o no. En nuestro ejemplo, incluimos el tipo de servicio que ofrecemos, un nombre que describa adecuadamente nuestro dispositivo a los humanos y el nivel de potencia de transmisión del dispositivo. Esto último suele ser útil para los clientes potenciales, porque pueden saber a qué distancia está nuestro dispositivo comparando la intensidad de la señal recibida con la anunciada.

Nota: El espacio para los datos publicitarios es muy limitado (sólo 31 bytes en total), por lo que los datos de longitud variable, como el nombre del dispositivo, deben ser lo más cortos posible.

QLowEnergyAdvertisingData advertisingData;
advertisingData.setDiscoverability(QLowEnergyAdvertisingData::DiscoverabilityGeneral);
advertisingData.setIncludePowerLevel(true);
advertisingData.setLocalName("HeartRateServer");
advertisingData.setServices(QList<QBluetoothUuid>() << QBluetoothUuid::ServiceClassUuid::HeartRate);

Configuración de los datos de servicio

A continuación configuramos el tipo de servicio que queremos ofrecer. Utilizamos el servicio Heart Rate tal y como se define en la especificación Bluetooth en su forma mínima, es decir, consistente únicamente en la característica Heart Rate Measurement. Esta característica debe soportar la propiedad Notify (y ninguna otra), y necesita tener un descriptor Client Characteristic Configuration, que permite a los clientes registrarse para recibir notificaciones sobre cambios en los valores de las características. Establecemos el valor inicial de la frecuencia cardíaca en cero, ya que no se puede leer de todos modos (la única forma en que el cliente puede obtener el valor es a través de notificaciones).

QLowEnergyCharacteristicData charData;
charData.setUuid(QBluetoothUuid::CharacteristicType::HeartRateMeasurement);
charData.setValue(QByteArray(2, 0));
charData.setProperties(QLowEnergyCharacteristic::Notify);
const QLowEnergyDescriptorData clientConfig(QBluetoothUuid::DescriptorType::ClientCharacteristicConfiguration,
                                            QByteArray(2, 0));
charData.addDescriptor(clientConfig);

QLowEnergyServiceData serviceData;
serviceData.setType(QLowEnergyServiceData::ServiceTypePrimary);
serviceData.setUuid(QBluetoothUuid::ServiceClassUuid::HeartRate);
serviceData.addCharacteristic(charData);

Publicidad y escucha de conexiones entrantes

Ahora que todos los datos han sido configurados, podemos empezar a hacer publicidad. Primero creamos un objeto QLowEnergyController en peripheral role y lo utilizamos para crear un objeto (dinámico) QLowEnergyService a partir de nuestro objeto (estático) QLowEnergyServiceData. A continuación, llamamos a QLowEnergyController::startAdvertising(). Nótese que entregamos nuestro QLowEnergyAdvertisingData dos veces: el primer argumento actúa como los datos reales de la publicidad, el segundo como los datos de la respuesta del escáner. Podrían transportar información diferente, pero aquí no tenemos necesidad de ello. También pasamos una instancia construida por defecto de QLowEnergyAdvertisingParameters, porque los parámetros de publicidad por defecto están bien para nosotros. Si un cliente está interesado en el servicio anunciado, puede establecer una conexión con nuestro dispositivo. Cuando esto ocurre, el dispositivo deja de anunciarse y se emite la señal QLowEnergyController::connected().

Nota: Cuando un cliente se desconecta, la publicidad no se reanuda automáticamente. Si quieres que eso ocurra, tienes que conectarte a la señal QLowEnergyController::disconnected() y llamar a QLowEnergyController::startAdvertising() en la ranura correspondiente.

bool errorOccurred = false;const std::unique_ptr<QLowEnergyController> leController(QLowEnergyController::createPeripheral());auto errorHandler = [&leController, &errorOccurred](QLowEnergyController::Error errorCode) {        qWarning().noquote().nospace() << errorCode << " occurred: "
           <<  leController->errorString(); if (errorCode! = QLowEnergyController::RemoteHostClosedError) {            qWarning("Heartrate-server quitting due to the error.");
            errorOccurred = true;     QCoreApplication::quit(); } };QObject::connect(leController.get(), &QLowEnergyController::errorOccurred, errorHandler); std::unique_ptr<QLowEnergyService> service(leController->addService(serviceData));  leController->startAdvertising(QLowEnergyAdvertisingParameters(), advertisingData,advertisingData);if (errorOccurred) return-1;

Proporcionar el Heartrate

Hasta aquí todo bien. Pero, ¿cómo obtiene realmente un cliente la frecuencia cardíaca? Esto sucede actualizando regularmente el valor de la característica respectiva en el objeto QLowEnergyService que recibimos de QLowEnergyController en el fragmento de código anterior. La fuente de la frecuencia cardíaca sería normalmente algún tipo de sensor, pero en nuestro ejemplo, simplemente inventamos valores que dejamos oscilar entre 60 y 100. La parte más importante del siguiente fragmento de código es la llamada a QLowEnergyService::writeCharacteristic. Si un cliente está conectado en ese momento y ha activado las notificaciones escribiendo en el mencionado Client Characteristic Configuration, recibirá una notificación sobre el nuevo valor.

QTimer heartbeatTimer;
quint8 currentHeartRate = 60;
enum ValueChange { ValueUp, ValueDown } valueChange = ValueUp;
const auto heartbeatProvider = [&service, &currentHeartRate, &valueChange]() {
    QByteArray value;
    value.append(char(0)); // Flags that specify the format of the value.
    value.append(char(currentHeartRate)); // Actual value.
    QLowEnergyCharacteristic characteristic
            = service->characteristic(QBluetoothUuid::CharacteristicType::HeartRateMeasurement);
    Q_ASSERT(characteristic.isValid());
    service->writeCharacteristic(characteristic, value); // Potentially causes notification.
    if (currentHeartRate == 60)
        valueChange = ValueUp;
    else if (currentHeartRate == 100)
        valueChange = ValueDown;
    if (valueChange == ValueUp)
        ++currentHeartRate;
    else
        --currentHeartRate;
};
QObject::connect(&heartbeatTimer, &QTimer::timeout, heartbeatProvider);
heartbeatTimer.start(1000);

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.