As you probably know, creating a mobile application that uses Bluetooth Low Energy to communicate with some external device, like heart rate, RC car or any other IoT device with BLE onboard, can be really tricky and time-consuming.

Curious why…?

Bluetooth Low Energy

Bluetooth Low Energy is a communication protocol that allows you to communicate with any IoT device with Bluetooth chip onboard and read and write information from that device using CBATT request and responses to do that.

In general, Bluetooth device is called Peripheral, and mobile app is a Center that can connect with given peripherals. Peripheral can advertise some advertisement data like name and known service. Services are the aggregates for characteristic. One peripheral can have few services and every service can one or more characteristic. From any characteristic you can or read the data, get notified when value is changed or write some data to that characteristic. The maximum size of the data that is transferred between peripheral and central is 20 bytes! Yes, bytes, not bites, not KiloBytes, not MegaBytes, just Bytes. Int64 have 8 bytes so you can send 2 Int64 values at once. Easy yes?

Device prototyping

Hardware prototype of that any IoT device can take a long, long time. You know PCB prototyping, PCB etching, sensor and chip soldering, creating second, third prototype… It will take some time.

After the prototype is ready, the software expert needs to prepare some low-level os with all the chips and sensor logic coded.

Everyone can make mistake so there is some additional time to debug and test that device. And all of this still before you will start working on the mobile app! But maybe not!

Swift Playgrounds for the rescue

Simulating the self-driving car BLE interface can be really easy and the only thing that you need to do that is the Bluetooth specification and your Mac machine.

Requirements

RC-car BLE interface specification:

RCCar Bluetooth Specification

Mac machine

Source: Wikipedia.org

Xcode + MacOS Playground

Let’s start

In the beginning, we need to create some abstraction for the CBPeripheralManager. Let’s discuss the following implementation.

TurnOn method is allocating the CBPeripheralManager, it is creating given object and assigning object delegate into self.

Before calling the turnOn method we need to register all the customs services and characteristics that we would like to include in the advertisement data. For that reason, I have prepared the `registerServiceController(_ serviceController: ServiceController)` method for that purpose.

When peripheralDidUpdateState method is called, and we are sure that given peripheral is On, we can start advertising data. Advertisement data includes:

`CBAdvertisementDataLocalNameKey` which is advertised name of given peripheral

`CBAdvertisementDataServiceUUIDsKey` which is publicly advertised service that given peripheral is sharing

TurnOff method is stopping the advertisement and removing reference for given peripheral.

Other delegate methods can be used to handle BLE reads and writes.

func peripheralManager(_ peripheral: CBPeripheralManager, didReceiveRead request: CBATTRequest) is performed when some external Bluetooth central (our app) would like to read data from given characteristic

func peripheralManager(_ peripheral: CBPeripheralManager, didReceiveWrite requests: [CBATTRequest]) is method that is invoked any time that some Bluetooth central is trying to write some data into given characteristic

func peripheralManager(_ peripheral: CBPeripheralManager, central: CBCentral, didSubscribeTo characteristic: CBCharacteristic) is called every time when someone would like to get notified for every change on given characteristic

Let’s check the ServiceController protocol requirements:

As you can see service controller is some kind of proxy that is transferring read/write and subscription request to given characteristic.

And here is our MotionService implementation:

As you can see, read request is transferred to movmentCharacteristic, just like any subscription request. Any writte request is ignored because data from that characteristic is read-only.

In object init, you can see that MotionService has a property called service which is a type of CBMutableService this object is added to the CBPeripheralManager just before it starts to advertise data. CBMutableService(type: CBUUID(string: “0x2FA2”), primary: true) init method gives us possibility to define the service UUID and primary option of the service.

In addition, every characteristic that is included in that service needs to be registered using CBMutableCharacteristic to that CBMutableService before Peripheral start to advertise.

Let’s check CharacteristicController requirements:

As you can see it’s very similar to the ServiceController protocol definition, so let’s check the example implementation:

And let’s make a small overview:

MotionCharacteristic has CBMutableCharacteristic property which is a CBMutableService characteristics array requirement, that property describe all actions that can be performed on the given characteristic:

CBMutableCharacteristic(type: CBUUID(string: “0x1a2b”), properties: [.read, .notify], value: nil, permissions: [.readable]). Here we need to define the UUID of given characteristic, properties: (read, write, notify), and permissions: (.readable, writeable).

To handle read request all you need to do is put new data value into request.data property. And call method on peripheral instance with following signature: peripheral.respond(to: request, withResult: .success)

To handle any change to be notified on given service we need to perform method on the CBPeripheralManager with a given signature: updateValue(Data, for: CBMutableCharacteristic, onSubscribedCentrals: nil).

That’s why we need to store the peripheral reference after handleSubscribeToCharacteristic(on peripheral: CBPeripheralManager) method is called.

Let’s put it all together:

And just after that we can be a peripheral master and force it to do what we want!

Let’s check the result using <a href=”https://itunes.apple.com/pl/app/nrf-connect/id1054362403?mt=8">nRF Connect — Nordic Semiconductor ASA</a>

That’s all!

Playground code can be found here: https://github.com/gregiOS/Playgrounds

Also, you can check my other article about Bluetooth Low Energy here:

https://blog.untitledkingdom.com/swift-playground-bluetooth-low-energy-8fe15eb2e6df