Implementasi CoreBluetooth untuk Komunikasi antara iOS dan BLE Peripheral
Skripsi mode = ON ๐
Kebetulan, skripsi saya menangkat tentang pengembangan aplikasi kontrol kendali kursi roda dengan pergerakan kepala. Pergerakan kepalanya sendiri dapat dikenali melalui pengolahan data dari sensor gerak yang ada dalam perangkat iPhone, seperti accelerometer sensor dan gyroscope sensor. Jadi, smartphone yang jadi pengontrol kursi rodanya tadi diikatkan di kepala dengan pengikat khusus maupun dipasangkan di VR cardboard.
Nah, aplikasi tadi berkomunikasi dengan perangkat kursi rodanya tadi menggunakan media bluetooth. Lebih tepatnya menggunakan Bluetooth Low Energy. Aplikasi tadi mengirimkan satu buah karakter yang nantinya akan diproses oleh mikrokontroler yang telah diprogram untuk menggerakkan kursi rodanya. Sambil menunggu mikrokontroler kontrol kursi roda yang sebenarnya, saya mencoba untuk mengontrol robot-robotan yang dipakaikan Arduino Nano dan modul BLE AT-09 (HM-10 clone). Terima kasih Pak Ratno (TIK UB), yang udah meminjamkan robot-robotannya untuk saya oprek ๐.
Jadi, perangkat BLE memiliki 2 role, role central dan role peripheral. Hubungan antara role central dan role peripheral akan dianalogikan sebagai berikut.
Anda kenal perangkat Xiaomi Mi Band? Mi Band bisa dikonfigurasi melalui aplikasi Mi Fit yang bisa diinstall di Android dan iOS. Smartphone dikategorikan sebagai central, karena dia melakukan komunikasi ke peripheral, mencari lalu tersambung ke peripheral. Sedangkan Mi Band dikategorikan sebagai peripheral, karena ia menerima input dari central.
Dan, setahu saya, tidak ada perangkat yang memiliki 2 role secara bersamaan, kalau tidak peripheral ya central, tidak bisa sebuah central menjadi peripheral secara bersamaan.
Implementasi CoreBluetooth
Supaya aplikasi bisa berkomunikasi dengan perangkat BLE, maka harus ada class yang conform ke protokol CBCentralManagerDelegate
dan CBPeripheralDelegate
. Class yang harus conform ke protokol tadi bisa subclass dari ViewController
atau kelas turunan NSObject
yang lain. Kedua protokol tadi digunakan untuk apa?
Protokol CBCentralManagerDelegate
diimplementasikan untuk melakukan tugas dari sebuah central, misalkan melakukan scanning peripheral terdekat, menyambungkan ke peripheral maupun memonitor perubahan state dari central, apakah hidup, mati, atau error.
Protokol CBPeripheralDelegate
diimplementasikan salah satunya untuk mendapatkan service dan characteristic dari sebuah BLE peripheral. Wait what, apa itu service dan characteristic?
Misalkan kita punya alat heart rate measurement, perangkat tersebut bisa saja memiliki 2 service, heart rate monitor dan device information. Tiap service pastilah memiliki satu atau lebih characteristic yang mana tiap characteristicnya menyimpan sebuah nilai, misalkan pada service device information punya salah satu characteristic yang bernama device vendor ID, yang berisikan 0x34AF misalkan.
Setiap peripheral, service dan characteristic pasti memiliki UUID sebagai identifiernya.
Alur dalam perangkat iOS melakukan komunikasi dengan BLE device adalah sebagai berikut:
- Cek terlebih dahulu apakah konektivitas bluetooth di smartphone telah diaktifkan.
- Jika telah aktif, maka lakukan scanning peripheral.
- Jika peripheral yang diinginkan telah ditemukan, maka sambungkan ke peripheral tersebut, dan hentikan proses scanning.
- Ketika telah terkoneksi, maka lanjutkan ke proses discovering services.
- Jika service yang diinginkan telah ditemukan, maka lanjutkan ke proses discovering characteristic.
- Jika characteristic yang diinginkan telah ditemukan, maka characteristic tersebut bisa kita gunakan untuk kirim/terima data dari/ke BLE peripheral.
Kalau dalam kasus di skripsi saya, aplikasi hanya butuh scanning untuk mencari satu peripheral saja (mikrokontroller untuk controller kursi roda).
// delegate method dari CBCentralManager delegate
func centralManagerDidUpdateState(_ manager: CBCentralManager) {
switch manager.state {
case .poweredOff:
print("BLE has powered off")
manager.stopScan()
case .poweredOn:
print("BLE is now powered on")
let serviceCBUUID = CBUUID(string: self.serviceUUIDString)
manager.scanForPeripherals(withServices: [serviceCBUUID], options: nil)
case .resetting: print("BLE is resetting")
case .unauthorized: print("Unauthorized BLE state")
case .unknown: print("Unknown BLE state")
case .unsupported: print("This platform does not support BLE")
}
}
Ketika peripheral telah terdeteksi, maka langkah selanjutnya adalah menyambungkan smartphone ke BLE peripheral, dan jangan lupa untuk stop proses scanning.
func centralManager(_ manager: CBCentralManager,
didDiscover peripheral: CBPeripheral,
advertisementData advertisement: [String : Any],
rssi: NSNumber) { self.selectedPeripheral = peripheral
manager.connect(self.selectedPeripheral, options: nil)
manager.stopScan()}
Ketika smartphone telah tersambung, maka langkah selanjutnya adalah untuk discovering service yang tersedia di peripheral tersebut.
func centralManager(_ central: CBCentralManager,
didConnect peripheral: CBPeripheral) { // ganti delegatenya sesuai objek yang conform ke
// CBPeripheralDelegate
peripheral.delegate = self
peripheral.discoverServices(nil)
}
Ketika telah menemukan service yang diinginkan, maka selanjutnya adalah untuk discovering characteristic dari service tersebut.
/* delegate dari CBPeripheralDelegate yang digunakan untuk discovering characteristic setelah service telah ditemukan */
func peripheral(_ peripheral: CBPeripheral,
didDiscoverServices error: Error?) {
guard let services = peripheral.services else { return }
for service in services {
if service.uuid.uuidString == self.serviceUUIDString {
peripheral.discoverCharacteristics(nil, for: service)
break
}
}
}/* delegate dari CBPeripheralDelegate yang digunakan handling ketika characteristic yang diinginkan telah ditemukan */
func peripheral(_ peripheral: CBPeripheral,
didDiscoverCharacteristicsFor service: CBService,
error: Error?) { guard let characteristics = service.characteristics else {
return
}
self.selectedService = service
for characteristic in characteristics {
if characteristic.uuid.uuidString == self.characteristicUUIDString {
self.selectedCharacteristic = characteristic
break
}
}
}
Ketika kita sudah menyimpan reference ke characteristic yang telah ditemukan, maka selanjutnya kita bisa menuliskan data ke peripheral dengan method writeValue(_:for:type:)
.
// contoh
peripheral.writeValue(data,
for: selectedCharacteristic,
type: .withoutResponse)
Source code lengkap untuk handle koneksi BLE diatas bisa dilihat di sini.
Kesimpulan
Dengan framework CoreBluetooth, aplikasi yang kita kembangkan bisa memanfaatkan teknologi BLE sebagai media komunikasi ke berbagai macam peripheral, seperti smart watch, heart rate measurement, maupun pada perangkat mikrokontroler yang lain.
Semoga bermanfaat ๐