Bagikan melalui


Bluetooth GATT Server

Topik ini menunjukkan cara menggunakan API Server Bluetooth Generic Attribute (GATT) untuk aplikasi Platform Windows Universal (UWP).

Penting

Anda harus mendeklarasikan kemampuan "bluetooth" di Package.appxmanifest.

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

API penting

Gambaran Umum

Windows biasanya beroperasi dalam peran klien. Namun demikian, banyak skenario muncul yang mengharuskan Windows untuk bertindak sebagai Bluetooth LE GATT Server juga. Hampir semua skenario untuk perangkat IoT, bersama dengan sebagian besar komunikasi BLE lintas platform akan mengharuskan Windows menjadi GATT Server. Selain itu, mengirim pemberitahuan ke perangkat yang dapat dipakai di dekatnya telah menjadi skenario populer yang membutuhkan teknologi ini juga.

Operasi server akan berputar di sekitar Penyedia Layanan dan GattLocalCharacteristic. Kedua kelas ini akan menyediakan fungsionalitas yang diperlukan untuk mendeklarasikan, menerapkan, dan mengekspos hierarki data ke perangkat jarak jauh.

Tentukan layanan yang didukung

Aplikasi Anda dapat mendeklarasikan satu atau beberapa layanan yang akan diterbitkan oleh Windows. Setiap layanan diidentifikasi secara unik oleh UUID.

Atribut dan UUID

Setiap layanan, karakteristik, dan deskriptor didefinisikan oleh UUID 128-bit uniknya sendiri.

API Windows semuanya menggunakan istilah GUID, tetapi standar Bluetooth mendefinisikan ini sebagai UUID. Untuk tujuan kami, kedua istilah ini dapat dipertukarkan sehingga kami akan terus menggunakan istilah UUID.

Jika atribut standar dan ditentukan oleh Bluetooth yang ditentukan SIG, atribut juga akan memiliki ID pendek 16-bit yang sesuai (misalnya, UUID Tingkat Baterai adalah 00002A19-0000-1000-8000-00805F9B34FB dan ID pendek adalah 0x2A19). UUID standar ini dapat dilihat di GattServiceUuids dan GattCharacteristicUuids.

Jika aplikasi Anda menerapkan layanan kustomnya sendiri, UUID kustom harus dibuat. Ini mudah dilakukan di Visual Studio melalui Alat -> CreateGuid (gunakan opsi 5 untuk mendapatkannya di "xxxxx-xxxx-... format xxxx"). Uuid ini sekarang dapat digunakan untuk mendeklarasikan layanan, karakteristik, atau deskriptor lokal baru.

Layanan Terbatas

Layanan berikut dicadangkan oleh sistem dan tidak dapat diterbitkan saat ini:

  1. Device Information Service (DIS)
  2. Layanan Profil Atribut Generik (GATT)
  3. Layanan Profil Akses Generik (GAP)
  4. Scan Parameters Service (SCP)

Mencoba membuat layanan yang diblokir akan mengakibatkan BluetoothError.DisabledByPolicy dikembalikan dari panggilan ke CreateAsync.

Atribut yang Dihasilkan

Deskriptor berikut dibuat secara otomatis oleh sistem, berdasarkan GattLocalCharacteristicParameters yang disediakan selama pembuatan karakteristik:

  1. Konfigurasi Karakteristik Klien (jika karakteristik ditandai sebagai tidak dapat diindikasikan atau dapat dipuji).
  2. Deskripsi Pengguna Karakteristik (jika properti UserDescription diatur). Lihat properti GattLocalCharacteristicParameters.UserDescription untuk informasi selengkapnya.
  3. Format Karakteristik (satu deskriptor untuk setiap format presentasi yang ditentukan). Lihat properti GattLocalCharacteristicParameters.PresentationFormats untuk informasi selengkapnya.
  4. Format Agregat Karakteristik (jika lebih dari satu format presentasi ditentukan). GattLocalCharacteristicParameters.Lihat properti PresentationFormats untuk informasi selengkapnya.
  5. Properti Yang Diperluas Karakteristik (jika karakteristik ditandai dengan bit properti yang diperluas).

Nilai deskriptor Extended Properties ditentukan melalui properti karakteristik ReliableWrites dan WritableAuxiliaries.

Mencoba membuat deskriptor yang dipesan akan menghasilkan pengecualian.

Perhatikan bahwa siaran saat ini tidak didukung. Menentukan Broadcast GattCharacteristicProperty akan menghasilkan pengecualian.

Membangun hierarki layanan dan karakteristik

GattServiceProvider digunakan untuk membuat dan mengiklankan definisi layanan utama akar. Setiap layanan memerlukan objek ServiceProvider sendiri yang mengambil GUID:

GattServiceProviderResult result = await GattServiceProvider.CreateAsync(uuid);

if (result.Error == BluetoothError.Success)
{
    serviceProvider = result.ServiceProvider;
    // 
}

Layanan utama adalah tingkat atas pohon GATT. Layanan utama berisi karakteristik serta layanan lain (disebut 'Termasuk' atau layanan sekunder).

Sekarang, isi layanan dengan karakteristik dan deskriptor yang diperlukan:

GattLocalCharacteristicResult characteristicResult = await serviceProvider.Service.CreateCharacteristicAsync(uuid1, ReadParameters);
if (characteristicResult.Error != BluetoothError.Success)
{
    // An error occurred.
    return;
}
_readCharacteristic = characteristicResult.Characteristic;
_readCharacteristic.ReadRequested += ReadCharacteristic_ReadRequested;

characteristicResult = await serviceProvider.Service.CreateCharacteristicAsync(uuid2, WriteParameters);
if (characteristicResult.Error != BluetoothError.Success)
{
    // An error occurred.
    return;
}
_writeCharacteristic = characteristicResult.Characteristic;
_writeCharacteristic.WriteRequested += WriteCharacteristic_WriteRequested;

characteristicResult = await serviceProvider.Service.CreateCharacteristicAsync(uuid3, NotifyParameters);
if (characteristicResult.Error != BluetoothError.Success)
{
    // An error occurred.
    return;
}
_notifyCharacteristic = characteristicResult.Characteristic;
_notifyCharacteristic.SubscribedClientsChanged += SubscribedClientsChanged;

Seperti yang ditunjukkan di atas, ini juga merupakan tempat yang baik untuk mendeklarasikan penanganan aktivitas untuk operasi yang didukung setiap karakteristik. Untuk menanggapi permintaan dengan benar, aplikasi harus menentukan dan mengatur penanganan aktivitas untuk setiap jenis permintaan yang didukung atribut. Gagal mendaftarkan handler akan mengakibatkan permintaan segera diselesaikan dengan UnlikelyError oleh sistem.

Karakteristik konstanta

Terkadang, ada nilai karakteristik yang tidak akan berubah selama masa pakai aplikasi. Dalam hal ini, disarankan untuk mendeklarasikan karakteristik konstan untuk mencegah aktivasi aplikasi yang tidak perlu:

byte[] value = new byte[] {0x21};
var constantParameters = new GattLocalCharacteristicParameters
{
    CharacteristicProperties = (GattCharacteristicProperties.Read),
    StaticValue = value.AsBuffer(),
    ReadProtectionLevel = GattProtectionLevel.Plain,
};

var characteristicResult = await serviceProvider.Service.CreateCharacteristicAsync(uuid4, constantParameters);
if (characteristicResult.Error != BluetoothError.Success)
{
    // An error occurred.
    return;
}

Menerbitkan layanan

Setelah layanan sepenuhnya ditentukan, langkah selanjutnya adalah menerbitkan dukungan untuk layanan. Ini menginformasikan OS bahwa layanan harus dikembalikan ketika perangkat jarak jauh melakukan penemuan layanan. Anda harus mengatur dua properti - IsDiscoverable dan IsConnectable:

GattServiceProviderAdvertisingParameters advParameters = new GattServiceProviderAdvertisingParameters
{
    IsDiscoverable = true,
    IsConnectable = true
};
serviceProvider.StartAdvertising(advParameters);
  • IsDiscoverable: Mengiklankan nama yang mudah diingat ke perangkat jarak jauh dalam iklan, membuat perangkat dapat ditemukan.
  • IsConnectable: Mengiklankan iklan yang dapat dihubungkan untuk digunakan dalam peran periferal.

Ketika layanan dapat ditemukan dan Dapat Dihubungkan, sistem akan menambahkan Uuid Layanan ke paket iklan. Hanya ada 31 byte dalam paket Iklan dan UUID 128-bit membutuhkan 16 di antaranya!

Perhatikan bahwa ketika layanan diterbitkan di latar depan, aplikasi harus memanggil StopAdvertising saat aplikasi ditangguhkan.

Menanggapi permintaan Baca dan Tulis

Seperti yang kita lihat di atas sambil mendeklarasikan karakteristik yang diperlukan, GattLocalCharacteristics memiliki 3 jenis peristiwa - ReadRequested, WriteRequested, dan SubscribedClientsChanged.

Read

Saat perangkat jarak jauh mencoba membaca nilai dari karakteristik (dan itu bukan nilai konstanta), peristiwa ReadRequested dipanggil. Karakteristik bacaan dipanggil serta arg (berisi informasi tentang perangkat jarak jauh) diteruskan ke delegasi:

characteristic.ReadRequested += Characteristic_ReadRequested;
// ... 

async void ReadCharacteristic_ReadRequested(GattLocalCharacteristic sender, GattReadRequestedEventArgs args)
{
    var deferral = args.GetDeferral();
    
    // Our familiar friend - DataWriter.
    var writer = new DataWriter();
    // populate writer w/ some data. 
    // ... 

    var request = await args.GetRequestAsync();
    request.RespondWithValue(writer.DetachBuffer());
    
    deferral.Complete();
}

Write

Ketika perangkat jarak jauh mencoba menulis nilai ke karakteristik, peristiwa WriteRequested dipanggil dengan detail tentang perangkat jarak jauh, yang karakteristik untuk ditulis dan nilai itu sendiri:

characteristic.ReadRequested += Characteristic_ReadRequested;
// ...

async void WriteCharacteristic_WriteRequested(GattLocalCharacteristic sender, GattWriteRequestedEventArgs args)
{
    var deferral = args.GetDeferral();
    
    var request = await args.GetRequestAsync();
    var reader = DataReader.FromBuffer(request.Value);
    // Parse data as necessary. 

    if (request.Option == GattWriteOption.WriteWithResponse)
    {
        request.Respond();
    }
    
    deferral.Complete();
}

Ada 2 jenis Penulisan - dengan dan tanpa respons. Gunakan GattWriteOption (properti pada objek GattWriteRequest) untuk mengetahui jenis penulisan mana yang dilakukan perangkat jarak jauh.

Mengirim pemberitahuan ke klien langganan

Yang paling sering dari operasi GATT Server, pemberitahuan melakukan fungsi penting mendorong data ke perangkat jarak jauh. Terkadang, Anda ingin memberi tahu semua klien berlangganan tetapi di lain waktu Anda mungkin ingin memilih perangkat mana yang akan dikirimi nilai baru:

async void NotifyValue()
{
    var writer = new DataWriter();
    // Populate writer with data
    // ...
    
    await notifyCharacteristic.NotifyValueAsync(writer.DetachBuffer());
}

Saat perangkat baru berlangganan pemberitahuan, peristiwa SubscribedClientsChanged akan dipanggil:

characteristic.SubscribedClientsChanged += SubscribedClientsChanged;
// ...

void _notifyCharacteristic_SubscribedClientsChanged(GattLocalCharacteristic sender, object args)
{
    List<GattSubscribedClient> clients = sender.SubscribedClients;
    // Diff the new list of clients from a previously saved one 
    // to get which device has subscribed for notifications. 

    // You can also just validate that the list of clients is expected for this app.  
}

Catatan

Aplikasi Anda bisa mendapatkan ukuran pemberitahuan maksimum untuk klien tertentu dengan properti MaxNotificationSize . Data apa pun yang lebih besar dari ukuran maksimum akan dipotong oleh sistem.

Saat Anda menangani peristiwa GattLocalCharacteristic.SubscribedClientsChanged , Anda dapat menggunakan proses yang dijelaskan di bawah ini untuk menentukan informasi lengkap tentang perangkat klien yang saat ini berlangganan: