Qt Bluetooth Low Energy API는 Qt 5.4에서 Central Role 장치를 위해 도입되었다. Qt 5.5 이후 API는 최종 버전이며 향후 릴리스에 대한 호환성 보장이 제공된다. Qt 5.7 이후로 Peripheral Role 장치를 지원하는 추가 API가 테크니컬 프리뷰로 추가되었으며 백엔드는 Linux/BlueZ, iOS 및 macOS용으로 구현되었다.
Bluetooth Smart라고도 하는 Bluetooth Low Energy는 2011년에 공식적으로 도입된 무선 컴퓨터 네트워크 기술이다. BLE 기술은 Bluetooth v4.0에서 도입되었으며 이 기술을 지원하는 장치를 Bluetooth Smart Ready 장치라고 한다. Classic Bluetooth와 동일한 2.4GHz 주파수에서 작동한다. 주요 차이점은 기술 이름에서 알 수 있듯이 저전력 소비 장치를 대상으로 한다는 점이다. 일반적으로 Bluetooth 저전력 장치는 코인 셀 배터리로 몇 달, 심지어 몇 년 동안 작동할 수 있다.
BLE 세계에서는 일반적으로 두역할로 나눠지는 디바이스가 있으며 다음과 같다.
또한 Bluetooth Low Energy는 ATT(속성 프로토콜) 및 GATT(일반 속성 프로필)의 두 가지 아주 중요한 프로토콜을 기반으로 한다. 모든 Bluetooth Smart Ready 장치에서 사용하는 통신 계층이다. 이와 관련해서는 별도의 Article 로 소개할 계획이다.
본 문서에서는 Qt에서 제공하는 Bluetooth Low Energy API를 사용하는 방법을 설명한다. 클라이언트 측에서 API는 주변 장치에 대한 연결 생성, 서비스 검색, 장치에 저장된 데이터 읽기 및 쓰기를 허용한다. 서버 측에서는 서비스를 설정하고, 광고하고, 클라이언트가 특성을 작성할 때 알림을 받을 수 있다.
주요 클래스
QLowEnergyController
QLowEnergyService
QLowEnergyCharacteristic
QLowEnergyDescriptor
근처에 있는 Bluetooth 장치를 검색하기 위해 QBluetoothDeviceDiscoveryAgent 생성한다.
QBluetoothDeviceDiscoveryAgent::start()
메서드를 호출하여 검색을 시작한다.
디바이스가 발견 될 때마다 deviceDiscovered(const QBluetoothDeviceInfo)
시그널이 방출된다.
완료되면 finished()
시그널이 방출 되며 이 때 discoveredDevices()
메서드를 호출하여 검색된 디바이스 정보를 리스트에 넣는다.
BLE 디바이스는 QLowEnergyController 를 생성하여 엑세스 할 수 있다. QLowEnergyController::createCentral(const QBluetoothDeviceInfo &)
를 통해 QLowEnergyController 생성한다.
QLowEnergyController::connectToDevice()
메서드를 통해 디바이스 연결을 설정한다. 연결되면 connected
시그널이 방출된다.
디바이스에서 제공하는 서비스를 알기 위해 discoverServices()
메서드를 호출한다.
새 서비스가 발견될 때마다 serviceDiscovered()
시그널이 발생되고 매개변수에는 발견된 서비스의 UUID 가 포함된다.
서비스 검색이 완료되면 discoveryFinished()
시그널이 방출된다.
QLowEnergyController::createServiceObject()
메서드에 서비스 UUID를 전달을 통해 QLowEnergyService
객체를 생성할 수 있다. 이 서비스 객체를 통해 서비스의 세부 정보에 대한 액세스를 제공한다.
서비스 개체가 처음 생성되면 해당 세부 정보가 아직 없으며 포함된 서비스, 특성 및 설명자의 검색은 discoverDetails()
를 호출할 때 트리거된다.
stateChanged()
또는 characteristicChanged()
시그널을 통해 모든 특성, Descriptor 및 include 서비스가 알려지고 읽거나 쓸 수 있는 시점을 알 수 있다.
...
// If heartRateService found, create new service
if (m_foundHeartRateService)
m_service = m_control->createServiceObject(QBluetoothUuid(QBluetoothUuid::HeartRate), this);
if (m_service) {
connect(m_service, &QLowEnergyService::stateChanged, this, &DeviceHandler::serviceStateChanged);
connect(m_service, &QLowEnergyService::characteristicChanged, this, &DeviceHandler::updateHeartRateValue);
connect(m_service, &QLowEnergyService::descriptorWritten, this, &DeviceHandler::confirmedDescriptorWrite);
m_service->discoverDetails();
} else {
setError("Heart Rate Service not found.");
}
...
마지막으로 Bluetooth Low Energy 표준에 따라 HeartRate 특성 값을 처리한다.
void DeviceHandler::updateHeartRateValue(const QLowEnergyCharacteristic &c, const QByteArray &value)
{
// ignore any other characteristic change -> shouldn't really happen though
if (c.uuid() != QBluetoothUuid(QBluetoothUuid::HeartRateMeasurement))
return;
auto data = reinterpret_cast<const quint8 *>(value.constData());
quint8 flags = *data;
//Heart Rate
int hrvalue = 0;
if (flags & 0x1) // HR 16 bit? otherwise 8 bit
hrvalue = static_cast<int>(qFromLittleEndian<quint16>(data[1]));
else
hrvalue = static_cast<int>(data[1]);
addMeasurement(hrvalue);
}
일반적으로 특성 값은 일련의 바이트이다. 이러한 바이트의 정확한 해석은 특성 및 값 구조에 따라 다르다. 이미 상당 수는 Bluetooth SIG에 의해 표준화되었지만 사용자 정의 프로토콜을 구현할 수도 있다. 위의 예제 코드는 표준화된 HeartRate 값을 읽는 방법을 보여준다.
주변 장치가 자신의 존재와 서비스를 알릴 필요가 있다. 주변 장치(슬레이브) 기능과 관련된 Qt Bluetooth Low Energy 클래스의 사용 방법을 보여준다.
주요 클래스
QLowEnergyAdvertisingData
QLowEnergyAdvertisingParameters
QLowEnergyServiceData
QLowEnergyCharacteristicData
QLowEnergyDescriptorData
QLowEnergyController
QLowEnergyService
서비스 및 특성은 QLowEnergyServiceData
, QLowEnergyCharacteristicData
및 QLowEnergyDescriptorData
클래스를 사용하여 달성할 수 있다. 정의될 BLE 서비스를 구성하는 필수 정보에 대한 컨테이너 또는 빌딩 블록 역할을 한다. 아래 코드 스니펫은 측정된 분당 비트를 게시하는 간단한 HeartRate 서비스를 정의한다. 이러한 서비스를 사용할 수 있는 예로 손목시계를 들 수 있다.
QLowEnergyCharacteristicData charData;
charData.setUuid(QBluetoothUuid::HeartRateMeasurement);
charData.setValue(QByteArray(2, 0));
charData.setProperties(QLowEnergyCharacteristic::Notify);
const QLowEnergyDescriptorData clientConfig(QBluetoothUuid::ClientCharacteristicConfiguration,
QByteArray(2, 0));
charData.addDescriptor(clientConfig);
QLowEnergyServiceData serviceData;
serviceData.setType(QLowEnergyServiceData::ServiceTypePrimary);
serviceData.setUuid(QBluetoothUuid::HeartRate);
serviceData.addCharacteristic(charData);
위의 예제코드에서 원하는 특성은 HeartRateMeasurement 이다. 애플리케이션은 심박수 변화를 측정하므로 특성에 대한 변경 알림을 활성화해야 한다. 모든 특성이 변경 알림을 제공하는 것은 아니다. HeartRate 특성이 표준화되었으므로 알림을 수신할 수 있다고 가정할 수 있다. 궁극적으로 QLowEnergyCharacteristic::properties()
에는 QLowEnergyCharacteristic::Notify
플래그가 설정되어 있어야 하며 적절한 알림의 가용성을 확인하기 위해 BluetoothUuid::ClientCharacteristicConfiguration
유형의 설명자가 있어야 한다.
광고 프로세스를 구성하는 데 두 가지 클래스가 사용된다. QLowEnergyAdvertisingData
에 포함된 정보는 현재 스캔 중인 다른 장치에서 볼 수 있다. 브로드캐스트할 정보를 지정하고 QLowEnergyAdvertisingParameters
는 광고 간격 설정 또는 연결이 허용되는 장치 제어와 같은 특정 측면을 위한 것이다. 광고 채널을 통해 전송된 실제 데이터 패킷은 31바이트를 초과할 수 없다.
주변 장치 객체는 QLowEnergyController::createPeripheral()
을 호출하여 생성한다. QLowEnergyController::startAdvertising()
을 호출하여 광고를 시작할 수 있다. 첫 번째 인수는 실제 광고 데이터 역할을 하고 두 번째 인수는 스캔 응답 데이터 역할을 한다.
QLowEnergyAdvertisingData advertisingData;
advertisingData.setDiscoverability(QLowEnergyAdvertisingData::DiscoverabilityGeneral);
advertisingData.setIncludePowerLevel(true);
advertisingData.setLocalName("HeartRateServer");
advertisingData.setServices(QList<QBluetoothUuid>() << QBluetoothUuid::HeartRate);
const QScopedPointer<QLowEnergyController> leController(QLowEnergyController::createPeripheral());
QScopedPointer<QLowEnergyService> service(leController->addService(serviceData));
leController->startAdvertising(QLowEnergyAdvertisingParameters(),
advertisingData, advertisingData);
클라이언트가 광고된 서비스에 관심이 있으면 이제 장치에 연결할 수 있다. 클라이언트가 연결을 시도하면 QLowEnergyController::connected()
시그널이 방출된다. 클라이언트 연결이 끊어지면 광고가 자동으로 재개되지 않으므로 QLowEnergyController::disconnected()
시그널에 연결하고 해당 슬롯에서 QLowEnergyController::startAdvertising()
을 호출해야 한다.
QLowEnergyController 로부터 받은 QLowEnergyService 객체의 각 특성 값을 정기적으로 업데이트함으로써 발생한다. 중요한 것은 QLowEnergyService::writeCharacteristic
에 대한 호출이다. 클라이언트가 현재 연결되어 있고 특성에 대한 알림 또는 표시를 활성화한 경우 해당 정보가 전송된다.
서버 예제: https://code.qt.io/cgit/qt/qtconnectivity.git/tree/examples/bluetooth/heartrate-server?h=5.15
클라이언트 예제: https://code.qt.io/cgit/qt/qtconnectivity.git/tree/examples/bluetooth/heartrate-game?h=5.15