Bluetooth Lessons II: Characteristics

Yesterday, I wrote about the basics involved in setting up a Bluetooth manager and scanning for available peripherals. The sample code left off after finding announced devices.

Last evening, I expanded this functionality to find a Mi wristband and execute a repeating vibration pattern. Today’s code is about four times longer and involves a lot more of the direct interaction with a BLE peripheral.

The changes start on finding a desired device. I looked at its peripheral.name, an optional value associated with a CBPeripheral. This approach is essentially the same as looking at SSIDs for WiFi. There’s not a lot of sophistication involved, and no pairing sequence. Once found, my code instructs the central manager to stop its scan (centralManager.stopScan()) and connect() to the matched peripheral.

At this point it’s really important that you do a few things:

  • Create a strong reference to the target peripheral. I used a property. Without this, the peripheral reference deallocates, as I found to my dismay.
  • Set the peripheral’s delegate, so you can monitor its callback routines, specifically centralManager(_:, didConnect:). You won’t be able to start the communication chain otherwise.

For the next step, request service discovery from the delegate callback. After the peripheral connects, call discoverServices(), and listen in peripheral(_:,didDiscoverServices:). At this point, the chain of command has passed from the manager that finds you the right peripheral, and moves to listening to the peripheral itself. Both components must establish delegation.

Today’s sample code uses a brute-force approach to find services, and then in the services callback, discover specific service characteristics (discoverCharacteristics(_:, for:)) for the device.

In production code, you’d limit these calls: supply a list of only those services and the characteristics (the actual API call points) you’re interested in. My code passes nil, because there aren’t that many calls for the target device and I don’t mind the extra overhead.

Like previous steps, characteristics have their own delegate method (peripheral(_:didDiscoverCharacteristicsFor:,error:)). Here’s where you can access and initialize specific actions that support those characteristics. I decided to use notifications to respond to the discovery of the vibration characteristic (“2A06”, an industry standard).

Notifications are short and sweet, especially for playgrounds. You won’t have to invest in designing protocols or implementing delegates. Just listen for the notification and then start doing whatever you need.

In this case, my open ended observer (it lives forever, so I don’t bother trying to save and release it) starts a vibration pattern by calling a custom type method startVibrating(degree:, delay:). This method writes request data to the peripheral’s characteristic, producing each vibration pattern on demand. The delay allows the vibration to repeat after an arbitrary number of seconds.

Like the peripheral, store your characteristics locally. Although they are characterized by a UUID, they aren’t meant to be built on the fly with raw UUID values — or at least not that I could find. Saving each characteristic for later reference appears to be a vital part of the set-up process.

At this point, I’ve pretty much taken this project where I need to. I can produce short and long vibration patterns, I’ve discovered the characteristic for direct vibration control (“FF05”, which appears to take a start and stop value), for testing the device (“FF0D”), and if I ever go that far, for pairing (“FF0F”).

I’m handing it off to my friend. Hopefully there’s enough functionality that he can perform buzzing-like therapy without further financial outlay.

Apparently, controlled buzzing has utility beyond (unconventional) autism therapy. There are any number of ADHD products built on the principle of re-focusing students back on-task every n minutes.

I suppose you could expand this as well with calendar integration for “take your meds” reminders or “get up and move around” ones, at a much lower price point than, for example, the full Apple Watch.

As for me, I discovered that I hate having anything on my wrists and that buzzing really gets on my nerves.  I’m glad I had the opportunity to play around with this, though.

If you end up building something interesting, please drop me a line and tell me about it.

2 Comments

  • Hi Erica,

    When you’re writing withoutResponse you won’t be getting response in didUpdateValueFor characteristic method.
    Sometimes you need to try writing with and without Response as BLE device will not respond if it doesn’t support certain type.

    Btw. I love that playground.

  • I’m currently working on a BLE project for a company that creates a peripheral that measures contractions during the later stages of pregnancy.

    If I could sum up my findings from my first BLE project it would be this: You Are Not In Charge. There are many other takeaways (use a background thread for your central manager, for instance) but the main one is you are not in charge.

    You might get data, and you might get it in sequence, and you might have many ideas of how the peripheral should be doing it’s job. But nothing is guaranteed.

    As a result, the idea of developing for the Happy Path first and adding error handling later is not viable in a real app. When starting out, especially with a peripheral that is also in development, there will be plenty of instances of things not working, and handling those becomes paramount if you want to avoid unhappy customers. The Happy Path will take care of itself.