logo
hero image

Bluetooth Low Energy (BLE) pada iOS

Transfer data hemat daya menggunakan Bluetooth Low Energy.
20 April 2024 · 12 Minutes

Mulai dari Earphone Wireless yang kita pakai untuk mendengarkan musik, Smartwatch yang kita gunakan untuk memonitor kesehatan kita, sampai dengan Beacon yang terdapat di museum, bisa hadir karena adanya teknologi Bluetooth Low Energy (BLE).

Pada artikel ini, kita akan belajar mengenai Apa itu BLE, Perbedaan BLE dengan Classic Bluetooth, Generic Attribute Profile (GATT), serta bagaimana cara membuat Peripheral dan Central menggunakan framework Core Bluetooth.

Apa itu BLE?

Bluetooth Low Energy atau yang biasa dikenal dengan BLE merupakan protokol terbaru dari Bluetooth yang mengoptimalkan kinerja Bluetooth dalam aspek efektivitas pengunaan daya sehingga akan mengonsumsi lebih sedikit daya daripada Bluetooth versi sebelumnya (Classic Bluetooth).

Umumnya beacon atau perangkat-perangkat IoT seperti sensor suhu itu harus mengirimkan data secara berkala, maka sumber daya atau energi listrik menjadi faktor yang sangat krusial, nah teknologi BLE hadir untuk menyelesaikan permasalahan tersebut karena teknologi BLE sangat efisien dalam pemakaian baterai.

BLE vs Classic Bluetooth

Perbedaan utama dari BLE dengan Classic Bluetooth adalah dari segi konsumsi sumber daya-nya. Sebagai perbandingan, 1 Watt Classic Bluetooth setara dengan 0.01-0.5 Watt BLE!

Kemudian latency BLE juga lebih rendah dibandingkan dengan Classic Bluetooth, BLE 6 ms, sedangkan Classic Bluetooth sekitar 100 ms.

Namun untuk jangkauan sinyal yang bisa dipancarkan lebih unggul Classic Bluetooth, yaitu sekitar 10-100 meter, sedangkan BLE maksimal hanya 10 meter saja.

Kekurangan lain dari BLE adalah mengenai Data Rate yang lebih rendah dibandingkan Classic Bluetooth yaitu sekitar 1-3 Mbps, sedangkan BLE hanya 1 Mbps.

Kemudian perbedaan terakhir adalah Classic Bluetooth perlu melakukan Pairing Device untuk melakukan transfer data, sedangkan BLE tidak perlu.

Dari beberapa perbedaan di atas, bisa disimpulkan bahwa Classic Bluetooth cocok digunakan untuk mengirimkan data antar device yang berukuran agak besar seperti video, dll dengan jarak yang agak jauh. Sedangkan BLE cocok untuk mengirimkan data secara berkala dalam waktu yang lama seperti sensor perangkat IoT dan Beacon.

Peripheral vs Central

Perangkat Bluetooth Low Energy (BLE) dapat memiliki peran antara menjadi Peripheral atau menjadi Central, tidak bisa keduanya. Peripheral bisa dianalogikan sebagai Server dan Central sebagai Client.

Contohnya adalah Smartwatch yang mengirimkan data detak jantung kepada HP kita, dalam contoh tersebut Smartwatch berperan sebagai Peripheral, dan HP kita berperan sebagai Central karena HP kita lah yang menerima data dari Smartwatch (Central).

Satu Peripheral dapat terhubung dengan beberapa Central, dan Satu Central dapat terhubung dengan beberapa Peripheral dalam satu waktu.

Generic Attribute Profile (GATT)

Generic Attribute Profile atau yang biasa disebut dengan GATT merupakan sebuah protokol yang mendefinisikan cara bagaimana 2 perangkat BLE untuk saling mengirim dan menerima data.

GATT ProfileGATT Profile

Sebuah Peripheral disebut juga sebagai GATT Server, karena berperan untuk menyediakan layanan kepada Central (GATT Client). GATT Server perlu mengimplementasikan GATT Profile dalam mendefiniskan layanan yang bisa digunakan oleh GATT Client.

Ada 5 bagian dalam GATT Profile, yaitu:

1. Profile

Profile merupakan bagian terluar pada GATT. Satu Profile dapat mempunyai beberapa Service. Contohnya adalah Profile Alat Pengukur Suhu mempunyai Service Suhu Celcius dan Service Suhu Fahrenheit.

2. Service

Service merupakan gabungan beberapa Characteristic yang memiliki keterkaitan. Contohnya adalah Service Suhu Celcius mempunyai Characteristic Rata-Rata Suhu Seminggu Terakhir, Characteristic Suhu Real-Time, dll.

3. Characteristic

Characteristic merupakan bagian utama pada GATT, satu Characteristic merepresentasikan satu layanan atau fitur yang ada pada Peripheral (GATT Server). Contohnya adalah Characteristic Rata-Rata Suhu Seminggu Terakhir.

4. Value

Value merupakan data yang terdapat pada Characteristic. Contohnya adalah Characteristic Rata-Rata Suhu Seminggu Terakhir mempunyai Value 30° Celcius.

5. Descriptor

Setiap Characteristic dapat mempunyai beberapa Descriptor. Sesuai namanya, Descriptor bertujuan untuk mendeskripsikan suatu Characteristic, misalnya fungsi dari Characteristic tersebut, format datanya, dll.

Setup Project

Sekarang, kita akan mengimplementasikan Bluetooth Low Energy pada iOS, Kita akan membuat aplikasi sederhana bernama "Bluetify" yang dapat berperan sebagai Peripheral atau sebagai Central.

Hasil akhirnya nanti akan seperti ini:

BluetifyBluetify

Untuk melakukan testing aplikasi "Bluetify" ini, kita memerlukan 2 buah iPhone, yang satu berperan sebagai Peripheral dan yang satunya sebagai Central.

Download Starter Project

Silakan download starter projectnya terlebih dahulu di link berikut, pilih branch "starter":

https://github.com/alfinsyahruddin/Bluetify/tree/starter

Jika sudah, silakan buka projectnya melalui Xcode.

Struktur Project

Struktur ProjectStruktur Project

Pada project "Bluetify", terdapat 2 folder utama yaitu "BluetifyKit" dan "UI".

1. BluetifyKit

BluetifyKit berisi logic utama yang ada pada aplikasi ini.

Folder Enums

  • Bluetify: Berisi konfigurasi aplikasi Bluetify, seperti Nama Peripheral, dll.

  • BluetifyUUID: Berisi kumpulan UUID yang ada pada Peripheral.

  • State: Berisi UI State aplikasi kita.

Folder Helpers

  • DataHelper: Digunakan untuk konversi tipe data Data ke tipe data lain dan sebaliknya.

BluetifyPeripheralKit.swift

Sebuah Class yang berfungsi untuk melakukan advertising data dan mengirim notifikasi kepada Central. Semua logic dan detail implementasi terkait dengan Peripheral akan kita tambahkan di class ini.

BluetifyCentralKit.swift

Sebuah Class yang berfungsi untuk melakukan scanning Peripheral, subscribe notifikasi dan mengirim message ke Peripheral. Semua logic dan detail implementasi terkait dengan Central akan kita tambahkan di class ini.

2. UI

Untuk membuat User Interface (UI) pada project ini, kita menggunakan SwiftUI karena syntax-nya sangat ringkas dan dapat mempercepat proses pembuatan UI jika dibandingkan dengan UIKit.

Folder Core

Berisi komponen-komponen UI yang dapat digunakan di banyak tempat.

Folder Pages

Berisi semua halaman yang ada pada aplikasi Bluetify.

Menambahkan Permission

Untuk menggunakan fitur Bluetooth pada aplikasi kita, kita perlu menambahkan 2 permission berikut pada file Info.plist:

Info.plist
1<key>NSBluetoothPeripheralUsageDescription</key>
2<string>For advertising data.</string>
3
4<key>NSBluetoothAlwaysUsageDescription</key>
5<string>For transfer data between devices.</string>

Membuat Peripheral

Silakan buka file "BluetifyPeripheralKit.swift". Untuk membuat Peripheral, kita akan memodifikasi isi dari file ini.

Peripheral Manager

Silakan tambahkan code berikut sebelum init:

icon
1private var peripheralManager: CBPeripheralManager!

dan code berikut di dalam init untuk membuat instance dari CBPeripheralManager

icon
1override init() {
2 super.init()
3
5 peripheralManager = CBPeripheralManager(delegate: self, queue: nil)
6}

Advertising

Advertising merupakan proses dari Peripheral untuk melakukan broadcast data kepada Central. Advertising bertujuan agar Central dapat mendeteksi Peripheral, melakukan koneksi dan bertukar data dengan Peripheral.

Silakan tambahkan code berikut di atas init:

icon
1private var deviceNameCharacteristic: CBMutableCharacteristic?
2private var messageCharacteristic: CBMutableCharacteristic?
3private var notificationCharacteristic: CBMutableCharacteristic?

Kemudian ubah implementasi dari function startAdvertising menjadi seperti ini:

icon
1private func startAdvertising() {
2 print("[PERIPHERAL] Start Advertising...")
3
4 peripheralManager.removeAllServices()
5
6 self.deviceNameCharacteristic = CBMutableCharacteristic(
7 type: BluetifyUUID.deviceNameCharacteristic.cbuuid,
8 properties: [.read],
9 value: nil,
10 permissions: [.readable]
11 )
12
13 self.deviceNameCharacteristic!.descriptors = [
14 CBMutableDescriptor(
15 type: CBUUID(string: CBUUIDCharacteristicUserDescriptionString),
16 value: NSString(string: "Characteristic for reading peripheral device names.")
17 )
18 ]
19
20 self.messageCharacteristic = CBMutableCharacteristic(
21 type: BluetifyUUID.messageCharacteristic.cbuuid,
22 properties: [.write],
23 value: nil,
24 permissions: [.writeable]
25 )
26
27 self.notificationCharacteristic = CBMutableCharacteristic(
28 type: BluetifyUUID.notificationCharacteristic.cbuuid,
29 properties: [.notify],
30 value: nil,
31 permissions: [.readable]
32 )
33
34 let mainService = CBMutableService(type: BluetifyUUID.mainService.cbuuid, primary: true)
35
36 mainService.characteristics = [
37 deviceNameCharacteristic,
38 messageCharacteristic,
39 notificationCharacteristic
40 ].compactMap { $0 }
41
42 peripheralManager.add(mainService)
43
44 state = .advertising
45 peripheralManager.startAdvertising([
46 CBAdvertisementDataLocalNameKey: Bluetify.peripheralName.rawValue,
47 CBAdvertisementDataServiceUUIDsKey: [BluetifyUUID.mainService.cbuuid]
48 ])
49}

Pada code di atas, kita membuat sebuah Peripheral yang mempunyai 1 service bernama mainService, dan mainService tersebut memiliki 3 Characteristics, yaitu:

  1. deviceNameCharacteristic: Informasi Nama Peripheral yang dapat dibaca oleh Central.

  2. messageCharacteristic: Message yang dapat dikirim oleh Central.

  3. notificationCharacteristic: Notifikasi yang akan dibroadcast ke Central.

Tiap Service dan Characteristic yang kita buat, kita perlu membuat UUID-nya. Pada project ini, kamu bisa melihat UUID tersebut pada enum BluetifyUUID.

Kita juga telah menambahkan Descriptor pada deviceNameCharacteristic dengan tipe CBUUIDCharacteristicUserDescriptionString yang bertujuan untuk memberi tahu Central mengenai deskripsi dari Characteristic tersebut.

Kamu bisa melihat tipe apa saja yang bisa digunakan untuk membuat Descriptor di sini: https://developer.apple.com/documentation/corebluetooth/cbdescriptor

Function startAdvertising akan kita panggil setelah bluetooth pada iPhone menyala. Silakan tambahkan code berikut pada delegate CBPeripheralManagerDelegate:

icon
1func peripheralManagerDidUpdateState(_ peripheral: CBPeripheralManager) {
2 print("[PERIPHERAL] State: \(peripheral.state)")
3
4 switch peripheral.state {
5 case .unknown, .unsupported, .unauthorized, .resetting, .poweredOff:
6 self.state = .bluetoothUnavailable
7 case .poweredOn:
8 self.startAdvertising()
9 @unknown default:
10 self.state = .bluetoothUnavailable
11 }
12}

Pada saat user keluar dari halaman Peripheral Mode, kita perlu memberhentikan Advertising. Silakan ubah implementasi dari function stopAdvertising menjadi seperti ini:

icon
1public func stopAdvertising() -> Void {
2 print("[PERIPHERAL] Stop Advertising.")
3
4 state = .bluetoothUnavailable
5 peripheralManager.stopAdvertising()
6}

Read

Agar Central dapat membaca Device Name dari Peripheral, silakan tambahkan code berikut pada delegate CBPeripheralManagerDelegate:

icon
1func peripheralManager(_ peripheral: CBPeripheralManager, didReceiveRead request: CBATTRequest) {
2 let characteristic = BluetifyUUID(rawValue: request.characteristic.uuid.uuidString)
3
4 switch characteristic {
5 case .deviceNameCharacteristic:
6 request.value = deviceName.asData()
7 default:
8 break
9 }
10
11 peripheral.respond(to: request, withResult: .success)
12}

Write

Agar Peripheral dapat menerima Message yang dikirim dari Central, silakan tambahkan code berikut pada delegate CBPeripheralManagerDelegate:

icon
1func peripheralManager(_ peripheral: CBPeripheralManager, didReceiveWrite requests: [CBATTRequest]) {
2 for request in requests {
3 let characteristic = BluetifyUUID(rawValue: request.characteristic.uuid.uuidString)
4
5 switch characteristic {
6 case .messageCharacteristic:
7 guard let newMessage = request.value?.asString() else { break }
8 print("[PERIPHERAL] New Message: \(newMessage)")
9
10 message = newMessage
11 default:
12 break
13 }
14
15 peripheral.respond(to: request, withResult: .success)
16 }
17}

Notification

Untuk mengirim notifikasi dari Peripheral ke Central, silakan ubah implementasi pada function sendNotification menjadi seperti ini:

icon
1public func sendNotification(_ text: String) -> Void {
2 guard let notificationCharacteristic else { return }
3
4 peripheralManager.updateValue(
5 text.asData(),
6 for: notificationCharacteristic,
7 onSubscribedCentrals: notificationCharacteristic.subscribedCentrals
8 )
9
10 print("[PERIPHERAL] Send Notification: \(text)")
11}

Membuat Central

Silakan buka file "BluetifyCentralKit.swift". Untuk membuat Central, kita akan memodifikasi isi dari file ini.

Central Manager

Silakan tambahkan code berikut sebelum init:

icon
1private var centralManager: CBCentralManager!
2private var peripheral: CBPeripheral?

dan code berikut di dalam init untuk membuat instance dari CBCentralManager

icon
1override init() {
2 super.init()
3
5 centralManager = CBCentralManager(delegate: self, queue: nil)
6}

Scanning

Scanning merupakan proses dari Central untuk mendeteksi Peripheral. Jika Peripheral terdeteksi, maka Central bisa melakukan koneksi ke Peripheral tersebut, dan setelah terhubung dengan Peripheral, Central dapat melakukan Read, Write, dan Subscribe Notification ke pada Peripheral yang terhubung.

Silakan tambahkan code berikut di atas init:

icon
1private var peripheral: CBPeripheral?
2private var deviceNameCharacteristic: CBCharacteristic?
3private var messageCharacteristic: CBCharacteristic?
4private var notificationCharacteristic: CBCharacteristic?

Kemudian ubah implementasi dari function startScanning menjadi seperti ini:

icon
1public func startScanning() -> Void {
2 print("[CENTRAL] Start Scanning...")
3 state = .scanning
4 centralManager.scanForPeripherals(withServices: [BluetifyUUID.mainService.cbuuid])
5}

Pada code di atas, kita melakukan scanning untuk mencari Peripheral yang mempunyai Service dengan UUID Main Service yang ada pada BluetifyUUID.

Function startScanning akan kita panggil setelah bluetooth pada iPhone menyala. Silakan tambahkan code berikut pada delegate CBCentralManagerDelegate:

icon
1func centralManagerDidUpdateState(_ central: CBCentralManager) {
2 print("[CENTRAL] State: \(central.state)")
3
4 switch central.state {
5 case .unknown, .unsupported, .unauthorized, .resetting, .poweredOff:
6 self.state = .bluetoothUnavailable
7 case .poweredOn:
8 self.startScanning()
9 @unknown default:
10 self.state = .bluetoothUnavailable
11 }
12}

Pada saat user keluar dari halaman Central Mode, kita perlu memberhentikan Scanning. Silakan ubah implementasi dari function stopScanning menjadi seperti ini:

icon
1public func stopScanning() -> Void {
2 print("[CENTRAL] Stop Scanning.")
3 centralManager.stopScan()
4}

Connection

Setelah proses Scanning, kita perlu melakukan koneksi terhadap Peripheral yang kita inginkan agar kita bisa bertukar data dengan Peripheral tersebut.

Pada delegate CBCentralManagerDelegate, silakan tambahkan code berikut:

icon
1func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String: Any], rssi RSSI: NSNumber) {
2 guard let peripheralName = peripheral.name else { return }
3
4 print("[CENTRAL] Peripheral: `\(peripheralName)`")
5
6 print("[CENTRAL] Connecting...")
7 centralManager.connect(peripheral, options: nil)
8
9 self.peripheral = peripheral
10}
11
12
13func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
14 print("[CENTRAL] Connected")
15
16 peripheral.delegate = self
17 peripheral.discoverServices(nil)
18
19 state = .connected
20}

Setelah berhasil terkoneksi dengan Peripheral, kita perlu mengimplementasikan delegate CBPeripheralDelegate

Silakan tambahkan code berikut pada delegate CBPeripheralDelegate:

icon
1func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
2 guard let services = peripheral.services else { return }
3
4 for service in services {
5 peripheral.discoverCharacteristics(nil, for: service)
6 }
7}
8
9func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
10 guard let characteristics = service.characteristics else { return }
11
12 print("[CENTRAL] Total characteristics: \(characteristics.count)")
13
14 for characteristic in characteristics {
15 if characteristic.uuid == BluetifyUUID.deviceNameCharacteristic.cbuuid {
16 self.deviceNameCharacteristic = characteristic
17 self.readDeviceName()
18 }
19
20 if characteristic.uuid == BluetifyUUID.messageCharacteristic.cbuuid {
21 self.messageCharacteristic = characteristic
22 }
23
24 if characteristic.uuid == BluetifyUUID.notificationCharacteristic.cbuuid {
25 self.notificationCharacteristic = characteristic
26 self.subscribeNotification()
27 }
28 }
29}

Pada code di atas, kita mencari 3 Characteristic, yaitu:

  1. deviceNameCharacteristic: Informasi Nama Peripheral yang akan dibaca oleh Central.

  2. messageCharacteristic: Message yang akan dikirim oleh Central.

  3. notificationCharacteristic: Notifikasi yang dibroadcast ke Central.

Pada saat pertama kali terhubung dengan Peripheral yang kita inginkan, kita panggil function readDeviceName untuk membaca Device Name dari Peripheral dan function subscribeNotification agar dapat menerima notifikasi dari Peripheral.

Read

Agar Central dapat membaca Device Name dari Peripheral, silakan tambahkan code berikut pada delegate CBPeripheralDelegate:

icon
1func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
2 if characteristic.uuid == BluetifyUUID.deviceNameCharacteristic.cbuuid {
3 let deviceName = characteristic.value?.asString() ?? "-"
4 print("[CENTRAL] DEVICE NAME: \(deviceName)")
5 self.deviceName = deviceName
6 }
7}

Dan ubah implementasi dari function readDeviceName menjadi seperti ini:

icon
1public func readDeviceName() -> Void {
2 guard let deviceNameCharacteristic else { return }
3 peripheral?.readValue(for: deviceNameCharacteristic)
4}

Write

Agar Central dapat mengirim Message ke Peripheral, silakan ubah implementasi dari function sendMessage menjadi seperti ini:

icon
1public func sendMessage(_ text: String) -> Void {
2 guard let messageCharacteristic else { return }
3 peripheral?.writeValue(text.asData(), for: messageCharacteristic, type: .withResponse)
4}

Notification

Agar dapat menerima notifikasi dari Peripheral, silakan tambahkan code berikut pada function peripheral(_:didUpdateValueFor:error:):

icon
1func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
2 if characteristic.uuid == BluetifyUUID.deviceNameCharacteristic.cbuuid {
3 let deviceName = characteristic.value?.asString() ?? "-"
4 print("[CENTRAL] DEVICE NAME: \(deviceName)")
5 self.deviceName = deviceName
6 }
7
9 if characteristic.uuid == BluetifyUUID.notificationCharacteristic.cbuuid {
10 let notification = characteristic.value?.asString() ?? "-"
11 print("[CENTRAL] NOTIFICATION: \(notification)")
12 self.notification = notification
13 }
15}

Dan ubah implementasi dari function subscribeNotification menjadi seperti ini:

icon
1public func subscribeNotification() -> Void {
2 guard let notificationCharacteristic else { return }
3 peripheral?.setNotifyValue(true, for: notificationCharacteristic)
4}

What's Next?

Sekarang kita telah belajar mengenai Apa itu BLE, Perbedaan BLE dengan Classic Bluetooth, Generic Attribute Profile (GATT), serta bagaimana cara membuat Peripheral dan Central menggunakan framework Core Bluetooth.

Selanjutnya, kamu bisa kembangkan lagi aplikasi Bluetify ini misalnya menambahkan Service dan Characteristic baru, melakukan pertukaran data di Mode Background, mengintegrasikan Core Bluetooth dengan WatchOS, dll.

Oke mungkin itu saja yang bisa saya bagikan kali ini, kalau kamu merasa artikel ini bermanfaat silakan Like & Share artikel ini ke teman-teman kamu atau jika kamu punya pertanyaan, tulis aja di kolom komentar, Thank you! 😁 🙏

iOS Development
Swift
Core Bluetooth
Bluetooth Low Energy

Written by :
Alfin Syahruddin
Developer · Stock Trader · Libertarian · Freethinker

Always open to new ideas. 🕊️

Loading...

Related articles

Articles that you might want to read.

hero image
Image Classification pada iOS

Tutorial Image Classification menggunakan Create ML dan Vision Framework.

18 June 2023 · 7 Minutes
iOS Development
Swift
Vision
Create ML
Artificial Intelligence
hero image
Testable Code

Menulis kode yang mudah untuk di-test pada Swift.

1 May 2023 · 8 Minutes
iOS Development
Swift
Testing
hero image
DocC Tutorial

Dokumentasikan project-mu dengan DocC!

19 March 2023 · 8 Minutes
iOS Development
Swift
DocC
CI/CD
All rights reserved © Alfin Syahruddin · 2019
RSS Feed