Beberapa tahun yang lalu, ketika saya pertama kali menemukan Bluetooth dalam proyek yang sedang berjalan, saya menemukan artikel ini, yang sangat membantu untuk memahami cara kerjanya, untuk menemukan titik "awal". Harapan yang berguna untuk pemula.
Tentang Pengarang: Yoav Schwartz adalah pengembang iOS terkemuka di Donkey Republic, sistem berbagi pengendara sepeda motor di Kopenhagen, berusaha untuk mengubah sikap terhadap bersepeda. Selanjutnya, kita akan berbicara atas nama penulis.
Pada artikel ini, saya akan berbicara tentang teknik praktis untuk bekerja dengan CoreBluetooth. Pertama, tentang Bluetooth Low Energy (BLE) karena tidak semua orang mengenal teknologi ini, kemudian tentang CoreBluetooth, kerangka kerja Apple yang memungkinkan kita berinteraksi dengan perangkat BLE. Saya juga akan memberi tahu Anda tentang beberapa trik perkembangan yang saya pelajari sendiri ketika men-debug, menangis dan merobek rambut dari kepala saya.
Bluetooth energi rendah
Sebagai permulaan, apa itu BLE? Ini seperti Bluetooth, yang kita semua gunakan pada speaker, headset, dll. Namun ada perbedaan - protokol ini hanya menghabiskan sedikit daya. Biasanya, satu pengisian baterai untuk perangkat yang mendukung BLE dapat bertahan selama berbulan-bulan atau bahkan bertahun-tahun (tentu saja tergantung pada bagaimana perangkat itu digunakan). Ini memungkinkan kami melakukan hal-hal yang sebelumnya tidak tersedia untuk Bluetooth "normal". Standar ini disebut Bluetooth 4.0, semuanya dimulai dengan teknologi yang disebut Smart Bluetooth, yang kemudian berkembang menjadi BLE. Ada
manual 200 halaman, Anda bisa membaca waktu tidur, bacaan yang mengasyikkan.
BLE sangat ekonomis dalam hal konsumsi energi, dan protokolnya sendiri tidak terlalu rumit. Jadi mengapa harus berdarah? Bagaimana kita bisa menggunakannya? Contoh pertama dan paling umum adalah sensor detak jantung. Biasanya, perangkat ini mengukur dan mentransmisikan detak jantung Anda melalui protokol. Ada juga segala macam sensor yang dapat Anda hubungkan melalui BLE dan membaca data yang mereka kumpulkan. Akhirnya, ada iBeacons yang dapat memberi tahu Anda "kedekatan" ke suatu tempat. Dalam tanda kutip karena pada iPhone Apple memblokir kemampuan untuk mendeteksi iBeacons sebagai perangkat Bluetooth biasa, jadi kami harus bekerja dengan CoreLocation. Secara umum, ini adalah Internet hal: Anda dapat terhubung ke TV atau AC, dan berkomunikasi dengannya menggunakan protokol ini.
Bagaimana cara kerjanya?
Kami memiliki perifer - ini adalah perangkat yang menggunakan protokol Bluetooth. Setiap perangkat memiliki layanan, bisa ada jumlah mereka, dan masing-masing memiliki karakteristik. Anda dapat mempertimbangkan perifer sebagai server. Dengan semua konsekuensi berikut: kadang-kadang mati, kadang-kadang butuh waktu untuk mentransfer data, dan kadang-kadang data ini tidak datang sama sekali.
Secara umum, kami memiliki layanan dengan banyak karakteristik, yang masing-masing berisi nilai, jenis, dan sebagainya. Untuk bekerja dengan CoreBluetooth, Anda tidak perlu tahu segalanya, yang terpenting adalah membaca data. Inilah yang kami coba dapatkan, modifikasi, atau gunakan untuk tujuan kami sendiri. Kami membutuhkan data dan pengetahuan ini tentang apa yang dapat kami lakukan dengannya.
Inilah pengantar singkat untuk BLE karena ada ribuan sumber daya yang menjelaskan fitur teknis lebih baik daripada saya.
Bluetooth inti
Core Bluetooth diperkenalkan oleh Apple sejak lama, di iOS 5. Apple mulai bekerja untuk memperkenalkan BLE ke dalam perangkatnya jauh lebih awal dari Android dan semakin populernya teknologi. Banyak pengembang menggunakan kerangka ini dalam aplikasi mereka, pada umumnya itu hanya pembungkus, karena protokol BLE sendiri cukup kompleks. Tidak juga, tapi percayalah, ini bukan sesuatu yang ingin saya kerjakan setiap hari. Sama seperti banyak hal lainnya, Apple membungkusnya dalam paket yang indah dan nyaman, memungkinkan Anda untuk menggunakan istilah yang kita semua bisa mengerti, pengembang bodoh.
Sekarang giliran untuk memberi tahu Anda apa yang benar-benar perlu Anda ketahui tentang kelas-kelas yang terlibat dalam berkomunikasi dengan kerangka kerja. Aktor utama kami adalah CBCentralManager, buat:
manager = CBCentralManager(delegate:self, queue:nil, options: nil)
Di atas, kami membuat manajer baru, menunjukkan delegasinya, kalau tidak kita tidak akan dapat menggunakannya. Kami juga menunjukkan antrian, dalam kasus kami nil, yang berarti bahwa semua komunikasi dengan manajer akan dilakukan pada antrian utama.
Anda perlu memahami apa yang sebenarnya akan Anda lakukan - menggunakan antrian terpisah akan mempersulit aplikasi, tetapi, tentu saja, pengguna akan lebih mencintai Anda. Jika Anda berencana untuk berkomunikasi hanya dengan satu perangkat, Anda tidak dapat mengganggu dan menggunakan antrian utama. Jika Anda masih ingin bereksperimen, buat antrian, tentukan di konstruktor dan jangan lupa kembali ke yang utama sebelum menggunakan hasil di tempat lain.
Opsi Tidak ada yang sangat menarik di sini, mungkin hal utama - ketika Anda membuat manajer dan bluetooth dimatikan untuk pengguna - aplikasi akan memberitahunya tentang hal itu, tetapi hampir semua orang mengklik "OK" (yang tidak benar-benar termasuk bluetooth), itulah sebabnya saya juga menggunakan opsi ini Saya tidak menggunakan.
Pertama-tama, setelah membuat manajer, delegasi memanggil metode:
func centralManagerDidUpdateState(_ central: CBCentralManager)
Jadi kami mendapat respons dari perangkat keras - apakah pengguna memiliki Bluetooth diaktifkan atau tidak.
Kiat pertama: manajer tidak berguna sampai kami mendapatkan jawaban bahwa bluetooth diaktifkan, kondisinya adalah. Status lain hanya dapat digunakan untuk meminta pengguna mengaktifkan bluetooth.
Pencarian perangkat
Sekarang manajer kami bekerja dengan baik, kita bisa menonton,
apa yang ada di sekitar kita (setelah menerima status .PoweredOn - kita sebut fungsi scanForPeripheralsWithServices :)
manager.scanForPeripheralsWithServices([CBUUID], options: nil)
Adapun layanan, ini adalah array dari CBUUIDs (kelas yang merupakan pengidentifikasi unik universal 128-bit untuk atribut yang digunakan oleh Bluetooth Low Energy per.), Yang kami gunakan sebagai filter untuk menemukan perangkat dengan hanya set UID ini, ini adalah praktik umum di CoreBluetooth .
Jika Anda melewatkan nil sebagai argumen, kita dapat melihat semua perangkat di sekitarnya. Untuk kinerja, tentu saja, lebih baik untuk menentukan array parameter yang kita butuhkan, tetapi dalam kasus ketika Anda tidak mengetahuinya, tidak ada hal buruk yang akan terjadi jika Anda melewati nol, tidak ada yang mati.
Karena kami meluncurkan pencarian untuk perangkat, kami harus menghentikannya. Kalau tidak, pencarian akan dilanjutkan dan mendaratkan baterai pengguna hingga kami menghentikannya. Segera setelah kami menemukan perangkat yang tepat, atau kebutuhan untuk pencarian menghilang, kami akan berhenti:
manager.stopScan()
Setiap kali perangkat baru ditemukan, delegasi manajer akan memanggil fungsi didDiscoverPeripheral pada antrian yang kami tentukan saat diinisialisasi. Fungsi mentransmisikan kepada kami perangkat yang ditemukan (periferal), informasi tentang hal itu (advertisementData - sesuatu yang diputuskan untuk ditunjukkan oleh pengembang chip setiap kali) dan level sinyal RSSI relatif dalam desibel.
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber)
Kiat kedua: selalu pertahankan tautan kuat ke perifer yang terdeteksi. Jika ini tidak dilakukan, sistem akan memutuskan bahwa kami tidak memerlukan perangkat yang ditemukan dan akan membuangnya. Dia akan mengingatnya, tetapi kita tidak akan lagi memiliki akses kepadanya. Jika tidak, kami tidak akan dapat bekerja dengan perangkat.
Koneksi perangkat
Kami menemukan perangkat yang kami minati - ini adalah cara datang ke pesta dan melihat seorang gadis cantik. Kami ingin terhubung, kami memanggil fungsi connectPeripheral - kami menawarkan "beli minuman". Jadi, kami mencoba untuk terhubung ke perangkat yang diinginkan (periferal), dan dapat memberi tahu kami "ya" atau "tidak", tetapi iPhone kami benar-benar bagus, sehingga kami akan mendengar jawaban positif.
manager.connectPeripheral(peripheral, options: nil)
Di sini kami beralih ke manajer yang bertanggung jawab atas koneksi, kami beri tahu dia perangkat mana yang kami sambungkan, dan sekali lagi kami berikan nihil ke opsi (jika Anda benar-benar sangat tertarik mempelajari opsi, baca dokumentasi, tetapi biasanya Anda bisa melakukannya tanpa itu). Ketika Anda selesai bekerja dengan perangkat, Anda dapat memutuskan koneksi darinya, yah, Anda tahu, di pagi hari - membatalkan Koneksi Perangkat:
//called to cancel and/or disconnect manager.cancelPeripheralConnection(peripheral)
Setelah kami terhubung atau terputus, delegasi akan memberi tahu kami tentang hal ini:
//didConnect func centralManager(central: CBCentralManager!, didConnectPeripheral peripheral: CBPeripheral!) //didDisconnect func centralManager(central: CBCentralManager!, didDisconnectPeripheral peripheral: CBPeripheral!, error: NSError!)
Sekarang, dua tips lebih penting. Protokol Bluetooth mengasumsikan batas waktu koneksi, tetapi Apple tidak peduli. iOS akan mencoba untuk terhubung lagi dan lagi dan tidak akan berhenti sampai Anda memanggil cancelPeripheralConnection. Proses ini dapat memakan waktu terlalu lama, sehingga perlu membatasi waktunya, dan jika, pada akhirnya, kami tidak menerima pesan koneksi yang berhasil (didConnectPeripheral), kami perlu memberi tahu pengguna bahwa ada masalah.
Jika Anda tidak menyimpan tautan yang kuat ke perangkat, iOS hanya akan mengatur ulang koneksi. Dari sudut pandangnya, ini berarti Anda tidak memerlukannya, dan mempertahankannya adalah tugas yang agak menghabiskan energi untuk baterai, dan kami tahu bagaimana Apple berhubungan dengan konsumsi energi.
Mari buat perangkat ini berguna
Jadi, kami terhubung ke perangkat, mari kita lakukan sesuatu dengannya. Sebelumnya, saya menyebutkan layanan dan fitur, nilai-nilai yang dikandungnya, itulah yang kami butuhkan. Sekarang kita memiliki perangkat, itu terhubung dan kita bisa mendapatkan layanannya dengan memanggil peripheral.discoverServices.
peripheral.discoverServices(nil) func peripheral(peripheral: CBPeripheral!, didDiscoverServices error: NSError!) peripheral.services
Sekarang kedengarannya agak membingungkan, tetapi delegasi dipanggil di utas yang kami tetapkan saat membuat manajer, terlepas dari kenyataan bahwa ini adalah delegasi ke pinggiran. Yaitu, sistem mengingat aliran yang bekerja dengannya, dan semua komunikasi Bluetooth kami berlangsung di aliran itu. Penting untuk tidak lupa kembali ke yang utama jika Anda tidak menggunakannya.
Kami mendapat layanan, tetapi kami masih belum bisa bekerja. Selanjutnya, Anda perlu memanggil peripheral.discoverCharacteristics, delegasi akan memberi kami semua karakteristik yang tersedia untuk layanan yang diterima di didDiscoverCharacteristicsForService. Sekarang kita bisa membaca nilainya,
yang terdapat di sana (readValueForCharacteristic) atau meminta untuk memberi tahu kami segera setelah sesuatu berubah di sana - setNotifyValue.
peripheral.discoverCharacteristics(nil, forService: (service as CBService)) func peripheral(peripheral: CBPeripheral!, didDiscoverCharacteristicsForService service: CBService!, error: NSError!) peripheral.readValueForCharacteristic(characteristic) peripheral.setNotifyValue(true, forCharacteristic: characteristic) func peripheral(peripheral: CBPeripheral!, didUpdateValueForCharacteristic characteristic: CBCharacteristic!, error: NSError!)
Tidak seperti Android, Apple tidak membedakan antara membaca dan pemberitahuan. Artinya, kami tidak tahu apa yang terjadi - kami membaca sesuatu dari perangkat atau perangkat ini memberi tahu kami sesuatu.
Merekam ke perangkat
Kami memiliki perangkat, kami membaca informasi darinya, mengelolanya. Jadi, kita dapat merekam informasi tentangnya, sebagai aturan, - NSData biasa. Anda hanya perlu mengetahui apa yang diharapkan perangkat ini dari kami dan apa yang akan diterima olehnya.
Sebagian besar perangkat BLE datang dengan spesifikasi, semacam API yang jelas cara "berkomunikasi" dengannya. Anda dapat menarik data dari karakteristik untuk mendapatkan setidaknya ide perkiraan tentang apa yang diharapkan perangkat dari kami.
Dari spesifikasi yang kita pelajari di mana karakteristik properti mana yang kita baca, dan di mana kita menulis, apakah kita akan diberitahu tentang perubahan (isNotifying). Lebih sering daripada tidak, di sini kita akan menemukan semua yang diperlukan untuk bekerja.
peripheral.writeValue(data: NSData!, forCharacteristic: CBCharacteristic!, type: CBCharacteristicWriteType) characteristic.properties - OptionSet type characteristic.isNotifying func peripheral(peripheral: CBPeripheral!, didWriteValueForCharacteristic characteristic: CBCharacteristic!, error: NSError!)
Selama proses perekaman, delegasi akan memberi tahu kami bahwa semuanya berjalan dengan baik (didWriteValueForCharacteristics), bahwa nilai yang diinginkan telah diperbarui, dan kami dapat memberi tahu pengguna tentang hal itu atau menggunakan informasi ini secara berbeda.
Kami mempertimbangkan topik di bagian yang sangat sempit, mengandalkan implementasi Apple, sehingga ada sejumlah masalah yang harus dihadapi. Misalnya, ketergantungan yang sangat kuat pada delegasi, begitu dicintai Apple.
Pewarisan CBPeripheral? Jika semuanya begitu mudah
Tampaknya karena kita memiliki perangkat, kita dapat mulai menggunakannya, tetapi pada kenyataannya itu tidak akan memberi tahu kita apa-apa tentang dirinya sendiri. Mungkin kita ingin mengontrol kunci, AC atau sensor detak jantung. Anda perlu tahu perangkat mana yang kami komunikasikan.
Sepertinya warisan: kami memiliki kasus khusus dari kesamaan. Dari pengalaman saya, saya dapat mengatakan bahwa ketika menggunakan warisan, sesuatu tidak akan bekerja sama seperti yang diharapkan, sesuatu tidak akan bekerja sama sekali, dan Anda tidak akan tahu mengapa. Secara umum, saya akan memperingatkan Anda terhadap gagasan mewarisi CBPeripheral. Apa yang harus dilakukan
Saya menyarankan Anda untuk menambahkan CBPeripheral ke konstruktor objek yang akan mengelolanya. Ini merangkumnya di dalam kelas ini. Gunakan untuk berinteraksi dengan perangkat, simpan tautan yang kuat agar iOS tidak memutuskan koneksi. Tetapi yang paling penting adalah bahwa kelas ini akan digunakan sebagai delegasi, jika tidak akan sulit untuk mengelola semua perangkat di satu tempat, ini mengancam banyak orang jika ada.
Menghubungkan dan bekerja dengan delegasi CBPeripheral
Jadi kami terhubung ke perangkat dan ingin menjadi CBPeripheralDelegate. Ada satu lagi nuansa: saat Anda bekerja dengan perangkat, "menginterogasi" layanan dan karakteristiknya, membaca dan menulis kepada mereka, hampir semua komunikasi terjadi dengan periferal. Semuanya kecuali koneksi.
Secara alami, kami ingin memusatkan semua komunikasi di satu tempat, tetapi manajer harus mengetahui apa yang terjadi dengan perangkat tersebut. Dan kesulitannya adalah memiliki satu sumber kebenaran, untuk memastikan bahwa setiap orang diberi tahu secara tepat waktu tentang apa yang terjadi dengan perangkat tersebut. Untuk melakukan ini, kami akan memantau status perangkat - itu dapat berubah dari terputus, terhubung, dan terhubung. Itu selalu memberi tahu Anda tentang situasi saat ini. Tetap berlangganan perubahan status di fasilitas kontrol kami, yang saya bicarakan sebelumnya, ini akan memungkinkan untuk berkomunikasi dengan perangkat dari satu tempat.
Kedekatan
Poin yang sangat penting, karena sulit untuk menemukan dokumentasi normal tentang topik ini. Dalam kasus Apple dan iBeacons mereka, semuanya sederhana, mereka memberi tahu kami seberapa dekat kita dengan perangkat bluetooth.
Sayangnya, kami tidak diberikan cara mudah untuk bekerja dengan perangkat pihak ketiga. Dan lebih dari sekali itu terjadi bahwa ada kebutuhan untuk menentukan perangkat terdekat. Juga sulit untuk memahami apakah perangkat dalam kisaran yang tersedia atau tidak. Kadang-kadang, ketika mencari perangkat, itu bisa membuat Anda tahu tentang dirinya sendiri hanya sekali dan menghilang, maka upaya untuk menyambung tidak akan berhasil.
Kami menggunakan metode berikut ini: menyimpan tumpukan dengan label tanggal dan kekuatan sinyal (RSSI) untuk setiap pesan yang diterima di discoverPeripheral. Jika seseorang menemukan CoreLocation, metode kami mirip dengan bagaimana cap waktu dan koordinat terkait disimpan di sana. Biasanya, semakin tinggi sinyal (RSSI), semakin dekat perangkat. Lebih sulit untuk memahami apakah suatu perangkat berada dalam jangkauan yang dapat diakses atau tidak, sebagian karena konsep ini sendiri cukup fleksibel. Untuk ini, saya menggunakan rata-rata tertimbang dari sinyal. Ingatlah bahwa kekuatan sinyal dari perangkat yang terhubung harus diminta secara manual setiap kali Anda perlu mengetahuinya.
Apa selanjutnya
Sayangnya, artikel ini tidak akan membuat Anda menjadi ahli jika Anda membacanya juga.
itu menjadi menarik - perhatikan
Panduan Pemrograman CoreBluetooth Apple , panduan ini tidak terlalu besar, tetapi sangat berguna. Masih ada beberapa siaran dari WWDC 2012 (
dasar dan
lanjutan ) dan satu dari
2013 , tetapi jangan khawatir, sedikit perubahan sejak itu.
Ada juga video dari
Altconf 2015 yang diposting di situs Realm, yang membagikan pengalaman John Sher, seorang pria hebat dan spesialis.