Bluetooth GATT 클라이언트Bluetooth GATT Client

이 문서에서는 일반적인 GATT 클라이언트 작업에 대 한 샘플 코드와 함께 UWP (GATT) 클라이언트 Api 앱에 대 유니버설 Windows 플랫폼 한 Bluetooth Generic 특성 ()을 사용 하는 방법을 보여 줍니다.This article demonstrates usage of the Bluetooth Generic Attribute (GATT) Client APIs for Universal Windows Platform (UWP) apps, along with sample code for common GATT client tasks:

  • 주변 장치에 대 한 쿼리Query for nearby devices
  • 장치에 연결Connect to device
  • 장치에서 지원 되는 서비스 및 특성 열거Enumerate the supported services and characteristics of the device
  • 특성에 대 한 읽기 및 쓰기Read and write to a characteristic
  • 특성 값이 변경 될 때 알림 구독Subscribe for notifications when characteristic value changes

중요

Appxmanifest.xml에서 "bluetooth" 기능을 선언 해야 합니다.You must declare the "bluetooth" capability in Package.appxmanifest.

<Capabilities> <DeviceCapability Name="bluetooth" /> </Capabilities>

중요 APIImportant APIs

개요Overview

개발자는 Windows 의 api를 사용 하 여 bluetooth LE 장치에 액세스할 수 있습니다.Developers can use the APIs in the Windows.Devices.Bluetooth.GenericAttributeProfile namespace to access Bluetooth LE devices. Bluetooth LE 장치는 다음 컬렉션을 통해 해당 기능을 노출 합니다.Bluetooth LE devices expose their functionality through a collection of:

  • 서비스Services
  • 특징Characteristics
  • 설명자Descriptors

서비스는 LE 장치의 기능 계약을 정의 하 고 서비스를 정의 하는 특성의 컬렉션을 포함 합니다.Services define the functional contract of the LE device and contain a collection of characteristics that define the service. 이러한 특성에는 특성을 설명 하는 설명자가 포함 됩니다.Those characteristics, in turn, contain descriptors that describe the characteristics. 이러한 3 가지 용어는 일반적으로 장치 특성 이라고 합니다.These 3 terms are generically known as the attributes of a device.

Bluetooth LE GATT Api는 원시 전송에 액세스 하는 대신 개체 및 함수를 노출 합니다.The Bluetooth LE GATT APIs expose objects and functions, rather than access to the raw transport. 또한 개발자는 GATT Api를 사용 하 여 다음 작업을 수행할 수 있는 기능을 통해 Bluetooth LE 장치를 사용할 수 있습니다.The GATT APIs also enable developers to work with Bluetooth LE devices with the ability to perform the following tasks:

  • 특성 검색 수행Perform attribute discovery
  • 특성 값 읽기 및 쓰기Read and Write attribute values
  • 특성 ValueChanged 이벤트에 대 한 콜백 등록Register a callback for Characteristic ValueChanged event

유용한 구현을 만들려면 개발자가 응용 프로그램에서 사용 하려는 GATT 서비스 및 특성에 대 한 사전 지식이 있어야 합니다 .이 특성 값은 API에서 제공 하는 이진 데이터가 사용자에 게 표시 되기 전에 유용한 데이터로 변환 됩니다.To create a useful implementation a developer must have prior knowledge of the GATT services and characteristics the application intends to consume and to process the specific characteristic values such that the binary data provided by the API is transformed into useful data before being presented to the user. Bluetooth GATT Api는 Bluetooth LE 장치와 통신 하는 데 필요한 기본 기본 형식만 노출 합니다.The Bluetooth GATT APIs expose only the basic primitives required to communicate with a Bluetooth LE device. 데이터를 해석 하려면 Bluetooth SIG 표준 프로필 또는 장치 공급 업체에서 구현 하는 사용자 지정 프로필에 의해 응용 프로그램 프로필을 정의 해야 합니다.To interpret the data, an application profile must be defined, either by a Bluetooth SIG standard profile, or a custom profile implemented by a device vendor. 프로필은 교환 된 데이터가 나타내는 내용과이를 해석 하는 방법에 대 한 응용 프로그램과 장치 간에 바인딩 계약을 만듭니다.A profile creates a binding contract between the application and the device, as to what the exchanged data represents and how to interpret it.

편의를 위해 Bluetooth SIG는 사용 가능한 공개 프로필의 목록을 유지 관리 합니다.For convenience the Bluetooth SIG maintains a list of public profiles available.

주변 장치에 대 한 쿼리Query for nearby devices

주변 장치에 대해 쿼리 하는 데는 두 가지 주요 방법이 있습니다.There are two main methods to query for nearby devices:

  • Windows의 DeviceWatcher. 열거형DeviceWatcher in Windows.Devices.Enumeration
  • AdvertisementWatcher의AdvertisementWatcher in Windows.Devices.Bluetooth.Advertisement

두 번째 방법은 광고 설명서의 길이에 설명 되어 있지만 여기서는 자세히 다루지 않지만 특정 광고 필터를 충족 하는 주변 장치의 Bluetooth 주소를 찾는 것이 기본 개념입니다.The 2nd method is discussed at length in the Advertisement documentation so it won't be discussed much here but the basic idea is to find the Bluetooth address of nearby devices that satisfy the particular Advertisement Filter. 주소가 있으면 BluetoothLEDevice. FromBluetoothAddressAsync 를 호출 하 여 장치에 대 한 참조를 가져올 수 있습니다.Once you have the address, you can call BluetoothLEDevice.FromBluetoothAddressAsync to get a reference to the device.

이제 DeviceWatcher 메서드로 돌아갑니다.Now, back to the DeviceWatcher method. Bluetooth LE 장치는 Windows의 다른 장치와 마찬가지로 열거 api를 사용 하 여 쿼리할 수 있습니다.A Bluetooth LE device is just like any other device in Windows and can be queried using the Enumeration APIs. Devicewatcher 클래스를 사용 하 여 찾을 장치를 지정 하는 쿼리 문자열을 전달 합니다.Use the DeviceWatcher class and pass a query string specifying the devices to look for:

// Query for extra properties you want returned
string[] requestedProperties = { "System.Devices.Aep.DeviceAddress", "System.Devices.Aep.IsConnected" };

DeviceWatcher deviceWatcher =
            DeviceInformation.CreateWatcher(
                    BluetoothLEDevice.GetDeviceSelectorFromPairingState(false),
                    requestedProperties,
                    DeviceInformationKind.AssociationEndpoint);

// Register event handlers before starting the watcher.
// Added, Updated and Removed are required to get all nearby devices
deviceWatcher.Added += DeviceWatcher_Added;
deviceWatcher.Updated += DeviceWatcher_Updated;
deviceWatcher.Removed += DeviceWatcher_Removed;

// EnumerationCompleted and Stopped are optional to implement.
deviceWatcher.EnumerationCompleted += DeviceWatcher_EnumerationCompleted;
deviceWatcher.Stopped += DeviceWatcher_Stopped;

// Start the watcher.
deviceWatcher.Start();

DeviceWatcher를 시작 하면 해당 장치에 대해 추가 된 이벤트에 대 한 처리기의 쿼리를 충족 하는 각 장치에 대 한 devicewatcher 을 받게 됩니다.Once you've started the DeviceWatcher, you will receive DeviceInformation for each device that satisfies the query in the handler for the Added event for the devices in question. DeviceWatcher에 대 한 자세한 내용은 Github에서전체 샘플을 참조 하세요.For a more detailed look at DeviceWatcher see the complete sample on Github.

장치에 연결Connecting to the device

원하는 장치가 검색 되 면 DeviceInformation.Id 를 사용 하 여 해당 장치에 대 한 Bluetooth LE 장치 개체를 가져옵니다.Once a desired device is discovered, use the DeviceInformation.Id to get the Bluetooth LE Device object for the device in question:

async void ConnectDevice(DeviceInformation deviceInfo)
{
    // Note: BluetoothLEDevice.FromIdAsync must be called from a UI thread because it may prompt for consent.
    BluetoothLEDevice bluetoothLeDevice = await BluetoothLEDevice.FromIdAsync(deviceInfo.Id);
    // ...
}

반면, 장치에 대 한 BluetoothLEDevice 개체에 대 한 모든 참조를 삭제 하면 (시스템의 다른 앱에 장치에 대 한 참조가 없는 경우) 작은 시간 제한 기간 후에 자동 연결 해제가 트리거됩니다.On the other hand, disposing of all references to a BluetoothLEDevice object for a device (and if no other app on the system has a reference to the device) will trigger an automatic disconnect after a small timeout period.

bluetoothLeDevice.Dispose();

앱에서 다시 장치에 액세스 해야 하는 경우 장치 개체를 다시 만들고 특성에 액세스 하기만 하면 (다음 섹션에서 설명) 필요한 경우 OS가 다시 연결 됩니다.If the app needs to access the device again, simply re-creating the device object and accessing a characteristic (discussed in the next section) will trigger the OS to re-connect when necessary. 장치가 근처에 있으면 장치에 대 한 액세스 권한을 얻게 됩니다. 그렇지 않으면 DeviceUnreachable 수 없음 오류가 반환 됩니다.If the device is nearby, you'll get access to the device otherwise it will return w/ a DeviceUnreachable error.

참고

이 메서드를 호출 하 여 BluetoothLEDevice 개체를 만드는 경우에는 연결을 시작 하지 않아도 됩니다.Creating a BluetoothLEDevice object by calling this method alone doesn't (necessarily) initiate a connection. 연결을 시작 하려면 Gattsession 을로 설정 true 하거나 BluetoothLEDevice에서 캐시 되지 않은 service discovery 메서드를 호출 하거나 장치에 대해 읽기/쓰기 작업을 수행 합니다.To initiate a connection, set GattSession.MaintainConnection to true, or call an uncached service discovery method on BluetoothLEDevice, or perform a read/write operation against the device.

  • MaintainConnection 가 true로 설정 된 경우 시스템은 연결을 무기한 대기 하 고 장치를 사용할 수 있을 때 연결 됩니다.If GattSession.MaintainConnection is set to true, then the system waits indefinitely for a connection, and it will connect when the device is available. Gattsession 는 속성 이므로 응용 프로그램이 대기 하는 것은 없습니다.There's nothing for your application to wait on, since GattSession.MaintainConnection is a property.
  • GATT의 서비스 검색 및 읽기/쓰기 작업의 경우 시스템은 유한 하지만 변수 시간을 기다립니다.For service discovery and read/write operations in GATT, the system waits a finite but variable time. 순간부터 분의 중요 한 항목입니다.Anything from instantaneous to a matter of minutes. 스택에서 트래픽을은 하 고 요청을 큐에 대기 하는 방법을 결정 합니다.Factors inclue the traffic on the stack, and how queued up the request is. 보류 중인 다른 요청이 없고 원격 장치에 연결할 수 없는 경우 시스템은 시간이 초과 될 때까지 7 초 동안 대기 합니다. 보류 중인 다른 요청이 있는 경우 큐의 각 요청을 처리 하는 데 7 초 정도 소요 될 수 있으므로 나중에 큐의 끝 부분을 향해 대기 시간이 길어집니다.If there are no other pending request, and the remote device is unreachable, then the system will wait for seven (7) seconds before it times out. If there are other pending requests, then each of the requests in the queue can take seven (7) seconds to process, so the further yours is toward the back of the queue, the longer you'll wait.

현재는 연결 프로세스를 취소할 수 없습니다.Currently, you can't cancel the connection process.

지원 되는 서비스 및 특성 열거Enumerating supported services and characteristics

이제 BluetoothLEDevice 개체를 만들었으므로 다음 단계는 장치에서 노출 하는 데이터를 검색 하는 것입니다.Now that you have a BluetoothLEDevice object, the next step is to discover what data the device exposes. 이 작업을 수행 하는 첫 번째 단계는 서비스를 쿼리 하는 것입니다.The first step to do this is to query for services:

GattDeviceServicesResult result = await bluetoothLeDevice.GetGattServicesAsync();

if (result.Status == GattCommunicationStatus.Success)
{
    var services = result.Services;
    // ...
}

관심이 있는 서비스를 확인 한 후 다음 단계는 특성을 쿼리 하는 것입니다.Once the service of interest has been identified, the next step is to query for characteristics.

GattCharacteristicsResult result = await service.GetCharacteristicsAsync();

if (result.Status == GattCommunicationStatus.Success)
{
    var characteristics = result.Characteristics;
    // ...
}

OS는 작업을 수행할 수 있는 GattCharacteristic 개체의 읽기 전용 목록을 반환 합니다.The OS returns a ReadOnly list of GattCharacteristic objects that you can then perform operations on.

특성에 대 한 읽기/쓰기 작업 수행Perform Read/Write operations on a characteristic

특성은 GATT 기반 통신의 기본 단위입니다.The characteristic is the fundamental unit of GATT based communication. 장치에 있는 데이터의 고유 부분을 나타내는 값을 포함 합니다.It contains a value that represents a distinct piece of data on the device. 예를 들어 배터리 수준 특성에는 장치의 배터리 수준을 나타내는 값이 있습니다.For example, the battery level characteristic has a value that represents the battery level of the device.

지원 되는 작업을 확인 하려면 특성 속성을 참조 하세요.Read the characteristic properties to determine what operations are supported:

GattCharacteristicProperties properties = characteristic.CharacteristicProperties

if(properties.HasFlag(GattCharacteristicProperties.Read))
{
    // This characteristic supports reading from it.
}
if(properties.HasFlag(GattCharacteristicProperties.Write))
{
    // This characteristic supports writing to it.
}
if(properties.HasFlag(GattCharacteristicProperties.Notify))
{
    // This characteristic supports subscribing to notifications.
}

읽기가 지원 되는 경우 값을 읽을 수 있습니다.If read is supported, you can read the value:

GattReadResult result = await selectedCharacteristic.ReadValueAsync();
if (result.Status == GattCommunicationStatus.Success)
{
    var reader = DataReader.FromBuffer(result.Value);
    byte[] input = new byte[reader.UnconsumedBufferLength];
    reader.ReadBytes(input);
    // Utilize the data as needed
}

특성에 대 한 쓰기는 비슷한 패턴을 따릅니다.Writing to a characteristic follows a similar pattern:

var writer = new DataWriter();
// WriteByte used for simplicity. Other common functions - WriteInt16 and WriteSingle
writer.WriteByte(0x01);

GattCommunicationStatus result = await selectedCharacteristic.WriteValueAsync(writer.DetachBuffer());
if (result == GattCommunicationStatus.Success)
{
    // Successfully wrote to device
}

: DataReaderdatawriter 여부 는 많은 Bluetooth api에서 가져온 원시 버퍼를 사용할 때 위한 됩니다.Tip: DataReader and DataWriter are indispensible when working with the raw buffers you get from many of the Bluetooth APIs.

알림 구독Subscribing for notifications

특성이 표시 또는 알림을 지원 하는지 확인 합니다 (특성 속성을 확인 하 여 확인).Make sure the characteristic supports either Indicate or Notify (check the characteristic properties to make sure).

제외: 각 값 변경 이벤트는 클라이언트 장치의 승인에 연결 되므로 표시를 더 안정적으로 간주 합니다.Aside: Indicate is considered more reliable because each value changed event is coupled with an acknowledgement from the client device. 대부분의 GATT 트랜잭션은 매우 안정적이 지 않고 전력을 절약 하므로 알림이 더 많이 발생 합니다.Notify is more prevalent because most GATT transactions would rather conserve power rather than be extremely reliable. 어떤 경우 든 모든는 컨트롤러 계층에서 처리 되므로 앱이 포함 되지 않습니다.In any case, all of that is handled at the controller layer so the app does not get involved. 단순히 "알림" 이라고 통칭 하지만 이제 알고 있습니다.We'll collectively refer to them as simply "notifications" but now you know.

알림을 받기 전에 다음 두 가지 작업을 수행 해야 합니다.There are two things to take care of before getting notifications:

  • 클라이언트 특성 구성 설명자 (CCCD)에 쓰기Write to Client Characteristic Configuration Descriptor (CCCD)
  • ValueChanged 이벤트를 처리 합니다.Handle the Characteristic.ValueChanged event

CCCD에 쓰려면이 클라이언트에서 특정 특성 값이 변경 될 때마다이를 확인 하려고 하는 서버 장치에 지시 합니다.Writing to the CCCD tells the Server device that this client wants to know each time that particular characteristic value changes. 가상 하드 디스크 파일에 대한 중요 정보를 제공하려면To do this:

GattCommunicationStatus status = await selectedCharacteristic.WriteClientCharacteristicConfigurationDescriptorAsync(
                        GattClientCharacteristicConfigurationDescriptorValue.Notify);
if(status == GattCommunicationStatus.Success)
{
    // Server has been informed of clients interest.
}

이제는 원격 장치에서 값이 변경 될 때마다 GattCharacteristic ValueChanged 이벤트가 호출 됩니다.Now, the GattCharacteristic's ValueChanged event will get called each time the value gets changed on the remote device. 모든 것은 처리기를 구현 하는 것입니다.All that's left is to implement the handler:

characteristic.ValueChanged += Characteristic_ValueChanged;

...

void Characteristic_ValueChanged(GattCharacteristic sender,
                                    GattValueChangedEventArgs args)
{
    // An Indicate or Notify reported that the value has changed.
    var reader = DataReader.FromBuffer(args.CharacteristicValue)
    // Parse the data however required.
}