<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Tinkering Down Under - bluetooth</title>
    <description>Notes from Tom VK7XT</description>
    <link>https://octet-stream.net/b/tdu/tag/bluetooth.html</link>
    <atom:link href="https://octet-stream.net/b/tdu/tag/bluetooth.xml" rel="self" type="application/rss+xml"/>
    <pubDate>Tue, 02 Sep 2025 09:44:50 +1000</pubDate>
    <lastBuildDate>Tue, 02 Sep 2025 09:44:50 +1000</lastBuildDate>
    <generator>Jekyll v4.1.1</generator>
    
      <item>
        <title>The waste that was COVIDSafe</title>
        <description>&lt;p&gt;&lt;a href=&quot;https://twitter.com/parisba/status/1557125500510023680?t=1_LsbAUudiVlIxdPwffjgg&quot;&gt;&lt;img src=&quot;https://octet-stream.net/b/tdu/assets/covidsafe-meme.png&quot; alt=&quot;&quot; width=&quot;100%&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It’s challenging to advocate for digital civil liberties in Australia. Every time the federal government tries to do something authoritarian, they make such a mess that it’s unclear whether it’s better to argue against it on moral grounds or because their harebrained scheme doesn’t actually deliver the dystopian outcome they wanted. Previous attempts include Stephen Conroy’s “clean feed” internet filter and Tony Abbott’s mandatory ISP data retention, the latter of which remains active law. Australia’s two major political parties contributed each of these ideas, enjoying broad electoral support all the while, which goes to show how doomed we are on this particular front.&lt;/p&gt;

&lt;p&gt;Fast-forward to COVID. Reverting to instinct and precedent, the Australian populace sat and waited to see what the government thought we should do in the face of this new challenge. Prime Minister Scott Morrison had an answer: install this wonderful new app &lt;em&gt;COVIDSafe&lt;/em&gt;, available on the Apple and Google app stores. Everybody’s phones will emit Bluetooth pings, and by tracking everyone who’s been nearby for 15+ minutes, all the close contacts of a positive case can be notified and we will quickly stop the spread of this virus. It was sold as the ticket to getting &lt;em&gt;back to normal&lt;/em&gt;. (To be fair, this sounded somewhat logical before Omicron.)&lt;/p&gt;

&lt;p&gt;Naturally, Australia’s implementation of this concept involved sending all of this proximity information to a big government database. Could it be a problem to create a central record of which citizens have been near which other citizens at particular dates and times? Surely not! At the time, Google and Apple were &lt;a href=&quot;https://en.wikipedia.org/wiki/Exposure_Notification&quot;&gt;collaborating on a system&lt;/a&gt; which enabled Android and iOS users to exchange these Bluetooth pings in an anonymous way until they were actually found to be positive, sidestepping most of the privacy concerns and most of the technical concerns which I’ll get to in a minute.&lt;/p&gt;

&lt;p&gt;If this Google/Apple system was adopted then the app would probably have provided some benefits. We could entertain ourselves rehashing familiar arguments about whether this level of data collection is justified for the public good and call it a day. Alas. The Australian government chose &lt;em&gt;not&lt;/em&gt; to adopt this technology.&lt;/p&gt;

&lt;p&gt;This meant COVIDSafe was functioning as a plain old Bluetooth LE app without special support for exposure notifications. In regular apps, Bluetooth Low Energy functionality is severely limited for reasons of privacy and battery life, particularly when the app is not running in the foreground or if the device is locked. So it was that COVIDSafe became another potential surveillance project foiled by its own rubbish implementation.&lt;/p&gt;

&lt;p&gt;Having committed to this course of action they inevitably faced a number of problems, all of which were foreseeable had they bothered to ask an independent subject matter expert. Transmission and reception of Bluetooth LE advertisements is heavily restricted when the relevant app is not actively running. iOS is the worst: when your app is backgrounded you get only a single notification the first time you detect another device. You don’t get to find out whether they moved closer, or when they moved away. I demonstrated this in a video at the time.&lt;/p&gt;

&lt;iframe title=&quot;Contact tracing in the background on iOS&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https://scitech.video/videos/embed/79965d3d-f788-4cbf-9c05-66e1d39c8a74&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups&quot;&gt;&lt;/iframe&gt;

&lt;p&gt;Then there is the general unreliability of rangefinding using Bluetooth LE. In the general case this is considered a fool’s errand, particularly on Android when there are lots of different manufacturers of wireless chipsets with their own sensitivity scales. It correlates with distance from the other device, certainly, but it also depends heavily on transmission power and antenna orientation and environmental factors. A colleague at a former employer spent days collecting Android calibration data for a client who wanted a one metre cutoff for an unrelated app. We could &lt;em&gt;kind&lt;/em&gt; of do it but it was rather rough.&lt;/p&gt;

&lt;p&gt;A couple of years later, &lt;a href=&quot;https://www.abc.net.au/news/2022-08-10/covidsafe-app-scrapped-what-went-wrong/101317746&quot;&gt;COVIDSafe is finally being decommissioned&lt;/a&gt;, having been an abject failure from beginning to end. Take-up was poor, efficacy was terrible, and the costs were high. This to say nothing of the public trust lost in having the Prime Minister spruik another boondoggle that runs down your phone battery and doesn’t actually work. (Actually, a distrust of government apps might prove useful if Peter Dutton ever becomes Prime Minister.)&lt;/p&gt;

&lt;p&gt;What this debacle indicates to me most is that the federal government needs a solid internal app development team. Making good-quality and privacy-respecting apps is nowadays a core responsibility of governments and we need people in the bureaucracy who will call it how it is when a minister wants something, and do the ongoing maintenance without costing tens of millions of dollars.&lt;/p&gt;
</description>
        <pubDate>Thu, 11 Aug 2022 19:55:00 +1000</pubDate>
        <link>https://octet-stream.net/b/tdu/blog/2022/08/11/the-waste-that-was-covidsafe.html</link>
        <guid isPermaLink="true">https://octet-stream.net/b/tdu/blog/2022/08/11/the-waste-that-was-covidsafe.html</guid>
        
        <category>bluetooth</category>
        
        
        <category>blog</category>
        
      </item>
    
      <item>
        <title>My wish for WWDC: a Bluetooth 5 API</title>
        <description>&lt;p&gt;Apple’s CoreBluetooth is pretty good. The implementation is high quality and it performs well. The consistent hardware and software means that you don’t get different behaviour from different models. It’s not perfect—the &lt;a href=&quot;https://developer.apple.com/documentation/corebluetooth/cbperipheralmanagerdelegate/1393248-peripheralmanagerisready&quot;&gt;flow control delegates&lt;/a&gt; are ugly and the advertisement configuration is limited—but it’s basically nice to use. Android is generally the opposite: buggy, slow, and devices from different manufacturers work differently. There is &lt;a href=&quot;https://github.com/iDevicesInc/SweetBlue/wiki/Android-BLE-Issues&quot;&gt;an eyebrow-raising list&lt;/a&gt; on SweetBlue’s old wiki page.&lt;/p&gt;

&lt;p&gt;One area Android is way ahead is Bluetooth 5. In case you missed it, Bluetooth 5 has several useful improvements over 4.2. The most interesting to me are the alternative PHYs, or modulation schemes. Traditional Bluetooth LE transmits at a rate of 1 Mbps. Bluetooth 5 adds a 2 Mbps PHY, so you can go twice as fast provided your signal is strong enough. There is also a Coded PHY, which includes forward error correction. This can operate at an effective rate of 500 kbps or 125 kbps, trading off speed for seriously improved range—reportedly over 1 km. In &lt;a href=&quot;https://www.youtube.com/watch?v=w4PbxVwg83M&quot;&gt;one video&lt;/a&gt; Nordic Semi put one of their chips on a drone and were receiving data at 750 metres with room to spare.&lt;/p&gt;

&lt;p&gt;Way back in 2017 Google released Android Oreo with new &lt;a href=&quot;https://developer.android.com/reference/kotlin/android/bluetooth/le/AdvertisingSetParameters.Builder#setsecondaryphy&quot;&gt;APIs to configure your PHYs&lt;/a&gt; and simultaneously support &lt;a href=&quot;https://source.android.com/devices/bluetooth/ble_advertising&quot;&gt;both “legacy” and extended&lt;/a&gt; advertisement sets. Things are not so peachy on the hardware side. You can have a device certified for Bluetooth 5 without supporting those new PHYs, meaning that Android phones produced in the last few years &lt;a href=&quot;https://www.xda-developers.com/check-bluetooth-5-all-features-supported/&quot;&gt;are all over the place in their support&lt;/a&gt;. The situation finally seems to be improving. In my testing, the Samsung Galaxy S9 doesn’t support all the BT5 features but the S10 does.&lt;/p&gt;

&lt;p&gt;Meanwhile, Apple hasn’t given us any control over Bluetooth 5 at all. Developers like me want to choose our PHYs. We want to use extended advertisements to transmit hundreds of bytes of data instead of ~23 plus a service UUID. We want to use periodic advertisements to avoid unnecessarily establishing connections. Hopefully, surely, finally this year we will see some updates on this at WWDC?&lt;/p&gt;

&lt;p&gt;While I’m dreaming, hopefully they’ll fix News+ too. I want to like it but I just can’t countenance publishers pushing ads in the middle of content I’m paying for.&lt;/p&gt;
</description>
        <pubDate>Thu, 27 May 2021 13:00:00 +1000</pubDate>
        <link>https://octet-stream.net/b/tdu/blog/2021/05/27/my-wish-for-wwdc-a-bluetooth-5-api.html</link>
        <guid isPermaLink="true">https://octet-stream.net/b/tdu/blog/2021/05/27/my-wish-for-wwdc-a-bluetooth-5-api.html</guid>
        
        <category>bluetooth</category>
        
        <category>programming</category>
        
        
        <category>blog</category>
        
      </item>
    
      <item>
        <title>Bluetooth LE for Mobile - 4. Android Peripheral</title>
        <description>&lt;p&gt;The story so far:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;a href=&quot;/b/tdu/blog/2018/02/20/bluetooth-le-mobile-1-an-overview.html&quot;&gt;An Overview&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/b/tdu/blog/2018/03/01/bluetooth-le-mobile-2-ios-peripheral.html&quot;&gt;iOS Peripheral&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/b/tdu/blog/2018/03/21/bluetooth-le-mobile-3-ios-central.html&quot;&gt;iOS Central&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Android Peripheral&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Next our discussion turns to Android. Android has had complete support for BLE/GATT for a while. It offers slightly more configurability than iOS in options such as power levels and advertisement data.&lt;/p&gt;

&lt;p&gt;There are some gotchas. Support for acting as a central &lt;a href=&quot;https://developer.android.com/guide/topics/connectivity/bluetooth-le&quot;&gt;was introduced in Android 4.3&lt;/a&gt; (API level 18). Support for acting as a peripheral &lt;a href=&quot;https://developer.android.com/about/versions/android-5.0#BluetoothBroadcasting&quot;&gt;didn’t arrive until Android 5.0&lt;/a&gt; (API level 21). I hope that you, dear reader, don’t need to support KitKat. If you do, it won’t be able to broadcast BLE advertisements in any case.&lt;/p&gt;

&lt;p&gt;There is also a significant number of bugs and performance problems. Some of this can be attributed to Android; much to poor implementations in specific hardware models. There is some voodoo involved in trying to keep the widest range of hardware happy. It involves things like judicious use of the main thread and attempting to gracefully work around the notorious &lt;em&gt;error 133&lt;/em&gt;. These problems afflict centrals more than peripherals so I’ll address them in the next post.&lt;/p&gt;

&lt;p&gt;While I can’t speak for the latest versions, matters seem to be improving with time. I’ve had the most trouble with Android 5.0 devices and somewhat less with 6.0. The difficulty with the Android ecosystem is that so many devices in the wild are stuck on old versions. At the time of writing 5.x still has a 22.4% market share and 6.0 has 25.5% (&lt;a href=&quot;https://fossbytes.com/most-popular-android-versions-always-updated/&quot;&gt;source&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;I wasn’t able to find any official walkthroughs of Android peripheral code and I don’t want to get into the Java here. For code examples I’ll refer a couple of times to &lt;a href=&quot;http://nilhcem.com/android-things/bluetooth-low-energy&quot;&gt;a nice blog post on nilhcem.com&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;android-permissions-for-bluetooth-le&quot;&gt;Android Permissions for Bluetooth LE&lt;/h3&gt;

&lt;p&gt;To use the full range of BLE functions an app must have the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;BLUETOOTH&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;BLUETOOTH_ADMIN&lt;/code&gt; privileges. See &lt;a href=&quot;https://developer.android.com/guide/topics/connectivity/bluetooth-le#permissions&quot;&gt;this documentation&lt;/a&gt; for a manifest example.&lt;/p&gt;

&lt;p&gt;More surprisingly, scanning on Android 6.0+ &lt;a href=&quot;https://developer.android.com/reference/android/bluetooth/le/BluetoothLeScanner#startScan(android.bluetooth.le.ScanCallback)&quot;&gt;requires&lt;/a&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ACCESS_COARSE_LOCATION&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ACCESS_FINE_LOCATION&lt;/code&gt;. The rationale for this is that BLE peripherals are often iBeacons representing particular locations. If the app happens to identify one then it knows where the user is. It… makes sense I suppose? Communicating this requirement effectively to users is left as an exercise for the reader.&lt;/p&gt;

&lt;h3 id=&quot;configuring-a-gatt-server&quot;&gt;Configuring a GATT Server&lt;/h3&gt;

&lt;p&gt;&lt;img src=&quot;https://octet-stream.net/b/tdu/assets/ble/android-gatt-server.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The process of setting up a tree of services and characteristics is conceptually very similar to on iOS. There is a class representing a single characteristic, another representing a single service, and another representing the overall GATT server (peripheral). You instantiate the “small” ones and add them to the bigger ones until the entire data model is defined.&lt;/p&gt;

&lt;p&gt;A &lt;a href=&quot;https://developer.android.com/reference/android/bluetooth/BluetoothGattCharacteristic.html#BluetoothGattCharacteristic(java.util.UUID,%20int,%20int)&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;BluetoothGattCharacteristic&lt;/code&gt; is initialised&lt;/a&gt; with a UUID, plus some properties and permissions flags. There are &lt;a href=&quot;https://developer.android.com/reference/android/bluetooth/BluetoothGattCharacteristic.html#PROPERTY_READ&quot;&gt;many possible flags&lt;/a&gt; but the most important ones concern reading and writing. If it’s readable it should have &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PROPERTY_READ&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PERMISSION_READ&lt;/code&gt;; if it’s writable it should &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PROPERTY_WRITE&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PERMISSION_WRITE&lt;/code&gt;. If it needs to support both reading and writing the flags can be bitwise-ORed together: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PERMISSION_READ | PERMISSION_WRITE&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The service is represented by a &lt;a href=&quot;https://developer.android.com/reference/android/bluetooth/BluetoothGattService&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;BluetoothGattService&lt;/code&gt;&lt;/a&gt;. Initialise one with the service UUID, and you will probably want to use the &lt;em&gt;primary&lt;/em&gt; service type unless you already know otherwise. The corresponding characteristics can then be added to it with &lt;a href=&quot;https://developer.android.com/reference/android/bluetooth/BluetoothGattService.html#addCharacteristic(android.bluetooth.BluetoothGattCharacteristic)&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;addCharacteristic()&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Finally, implement &lt;a href=&quot;https://developer.android.com/reference/android/bluetooth/BluetoothGattServerCallback&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;BluetoothGattServerCallback&lt;/code&gt;&lt;/a&gt; and &lt;a href=&quot;https://developer.android.com/reference/android/bluetooth/BluetoothManager#openGattServer(android.content.Context,%20android.bluetooth.BluetoothGattServerCallback)&quot;&gt;open&lt;/a&gt; a &lt;a href=&quot;https://developer.android.com/reference/android/bluetooth/BluetoothGattServer&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;BluetoothGattServer&lt;/code&gt;&lt;/a&gt;, providing your callback. Services can be added to it with &lt;a href=&quot;https://developer.android.com/reference/android/bluetooth/BluetoothGattServer.html#addService(android.bluetooth.BluetoothGattService)&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;addService()&lt;/code&gt;&lt;/a&gt;. It won’t actually broadcast any advertisements – we’ll get to that in a moment. Your callback implementation is what does all the work of handling connected devices and responding to read and write requests.&lt;/p&gt;

&lt;p&gt;See &lt;a href=&quot;http://nilhcem.com/android-things/bluetooth-low-energy#creating-the-gatt-service&quot;&gt;the nilhcem.com blog post&lt;/a&gt; for a code example of configuring characteristics and services, as well as handling read and write requests. Note that they are also using a feature called characteristic descriptors which I haven’t discussed. It isn’t critical if you’re writing both the central and peripheral app which means you know exactly what the characteristics represent.&lt;/p&gt;

&lt;h3 id=&quot;advertising-with-bluetoothleadvertiser&quot;&gt;Advertising with BluetoothLeAdvertiser&lt;/h3&gt;

&lt;p&gt;&lt;img src=&quot;https://octet-stream.net/b/tdu/assets/ble/android-broadcast.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;A separate class called &lt;a href=&quot;https://developer.android.com/reference/android/bluetooth/le/BluetoothLeAdvertiser&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;BluetoothLeAdvertiser&lt;/code&gt;&lt;/a&gt; does the specific job of broadcasting advertisements. You obtain one &lt;a href=&quot;https://developer.android.com/reference/android/bluetooth/BluetoothAdapter#getBluetoothLeAdvertiser()&quot;&gt;via &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;BluetoothAdapter&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To call &lt;a href=&quot;https://developer.android.com/reference/android/bluetooth/le/BluetoothLeAdvertiser.html#startAdvertising(android.bluetooth.le.AdvertiseSettings,%20android.bluetooth.le.AdvertiseData,%20android.bluetooth.le.AdvertiseCallback)&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;startAdvertising()&lt;/code&gt;&lt;/a&gt; you need to prepare an &lt;a href=&quot;https://developer.android.com/reference/android/bluetooth/le/AdvertiseSettings&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AdvertiseSettings&lt;/code&gt;&lt;/a&gt; using &lt;a href=&quot;https://developer.android.com/reference/android/bluetooth/le/AdvertiseSettings.Builder&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AdvertiseSettings.Builder&lt;/code&gt;&lt;/a&gt;, &lt;a href=&quot;https://developer.android.com/reference/android/bluetooth/le/AdvertiseData&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AdvertiseData&lt;/code&gt;&lt;/a&gt; using &lt;a href=&quot;https://developer.android.com/reference/android/bluetooth/le/AdvertiseData.Builder&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AdvertiseData.Builder&lt;/code&gt;&lt;/a&gt;, and implement the &lt;a href=&quot;https://developer.android.com/reference/android/bluetooth/le/AdvertiseCallback&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AdvertiseCallback&lt;/code&gt;&lt;/a&gt; interface. This callback class will find out whether &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;startAdvertising()&lt;/code&gt; was successful or not. Like iOS, this is an asynchronous process and you should wait for the callback rather than assume it worked.&lt;/p&gt;

&lt;p&gt;The &lt;a href=&quot;http://nilhcem.com/android-things/bluetooth-low-energy#start-advertising&quot;&gt;“Start Advertising” section of the nilhcem blog post&lt;/a&gt; has a good example of preparing typical parameters.&lt;/p&gt;

&lt;h3 id=&quot;extra-advertisement-data-with-scan-response&quot;&gt;Extra Advertisement Data with Scan Response&lt;/h3&gt;

&lt;p&gt;Android provides considerably more control over how advertisement packets are structured than iOS does. As seen above, an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AdvertiseData.Builder&lt;/code&gt; is used to construct the contents of the advertisement packet. This can include arbitrary “manufacturer data”, which is really cool. The trouble is that you can only have 31 total bytes of data in the packet. This is really tight and every field you add has overhead. Service UUIDs are really big unless your company can obtain &lt;a href=&quot;https://www.bluetooth.com/specifications/assigned-numbers/service-discovery&quot;&gt;a short UUID allocation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;One solution is an active scan request followed by a scan response. If a central hears a peripheral’s advertisement it can essentially call out “hey can I have a bit more info please?” The peripheral will oblige by broadcasting a second advertising packet. This scan response can contain another 31 bytes, separate from the original. These content of these two packets is merged when you receive it on a central.&lt;/p&gt;

&lt;p&gt;On Android you provide the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;scanResponse&lt;/code&gt; packet as a second instance of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AdvertiseData&lt;/code&gt;. There is a corresponding &lt;a href=&quot;https://developer.android.com/reference/android/bluetooth/le/BluetoothLeAdvertiser.html#startAdvertising(android.bluetooth.le.AdvertiseSettings,%20android.bluetooth.le.AdvertiseData,%20android.bluetooth.le.AdvertiseData,%20android.bluetooth.le.AdvertiseCallback)&quot;&gt;version of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;startAdvertising()&lt;/code&gt;&lt;/a&gt; that takes two &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AdvertiseData&lt;/code&gt; parameters. Conceptually:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://octet-stream.net/b/tdu/assets/ble/android-scan-response.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;advertising-rate-and-power&quot;&gt;Advertising Rate and Power&lt;/h3&gt;

&lt;p&gt;Android offers the developer control over the rate at which advertisements are broadcast – and it defaults to a low rate, which could take centrals a relatively long time to detect. This behaviour is controlled by the “mode” in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AdvertiseSettings&lt;/code&gt;. There are &lt;a href=&quot;https://developer.android.com/reference/android/bluetooth/le/AdvertiseSettings.html#ADVERTISE_MODE_BALANCED&quot;&gt;three constants&lt;/a&gt; corresponding to “low power” (the default), “balanced”, and “low latency”.&lt;/p&gt;

&lt;p&gt;The exact rates are not specified in the documentation. I have always used the highest “low latency” rate. Anecdotally this produces a stream of advertisements at around 10 Hz. The others are reported to go down to ~4 Hz and 1 Hz. For comparison, iOS apps advertise at a steady 20 Hz.&lt;/p&gt;

&lt;p&gt;What rate does your peripheral actually need? If the central user is going to open and interact with your app in order to locate nearby peripherals, their receiving radio will be fully powered. They will probably pick up even a 1 Hz advertisement within a second or two depending on ambient interference. If you’re relying on longer-term background scanning where the OS will be cutting corners to save power, more frequent advertisements means a better chance of being detected promptly.&lt;/p&gt;

&lt;p&gt;The power level can also be adjusted using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AdvertiseSettings&lt;/code&gt;. If it only makes sense for your app to communicate at short distances consider using &lt;a href=&quot;https://developer.android.com/reference/android/bluetooth/le/AdvertiseSettings.html#ADVERTISE_TX_POWER_ULTRA_LOW&quot;&gt;a low power level&lt;/a&gt;. This will save energy, help to reduce interference with other Bluetooth devices, and require your centrals to get closer before they detect the peripheral.&lt;/p&gt;

&lt;h3 id=&quot;summary&quot;&gt;Summary&lt;/h3&gt;

&lt;p&gt;This post has been a quick introduction to BLE on Android, the basic steps to act as a peripheral, and a look at some more advanced configuration options.&lt;/p&gt;

&lt;p&gt;In the next post I will describe using the central role on Android, along with some guidelines for reducing the number of problems on older devices.&lt;/p&gt;
</description>
        <pubDate>Mon, 02 Jul 2018 15:30:00 +1000</pubDate>
        <link>https://octet-stream.net/b/tdu/blog/2018/07/02/bluetooth-le-mobile-4-android-peripheral.html</link>
        <guid isPermaLink="true">https://octet-stream.net/b/tdu/blog/2018/07/02/bluetooth-le-mobile-4-android-peripheral.html</guid>
        
        <category>bluetooth</category>
        
        <category>programming</category>
        
        
        <category>blog</category>
        
      </item>
    
      <item>
        <title>Bluetooth LE for Mobile - 3. iOS Central</title>
        <description>&lt;p&gt;The story so far:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;a href=&quot;/b/tdu/blog/2018/02/20/bluetooth-le-mobile-1-an-overview.html&quot;&gt;An Overview&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/b/tdu/blog/2018/03/01/bluetooth-le-mobile-2-ios-peripheral.html&quot;&gt;iOS Peripheral&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;iOS Central&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/b/tdu/blog/2018/07/02/bluetooth-le-mobile-4-android-peripheral.html&quot;&gt;Android Peripheral&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The last post focused on how to make an iOS device become a BLE &lt;em&gt;peripheral&lt;/em&gt;. This one will discuss being a &lt;em&gt;central&lt;/em&gt;. There were some general tips for iOS too so if you’re jumping in here I’d recommend at least reading the previous post.&lt;/p&gt;

&lt;p&gt;iPhones and iPads make fantastic centrals. It doesn’t matter so much what the peripheral is – as centrals they are reliable, fast, and in most cases recover cleanly from error conditions. My first time doing BLE was on iOS communicating with a &lt;a href=&quot;https://punchthrough.com/bean&quot;&gt;LightBlue Bean&lt;/a&gt; and I thought this level of reliability was normal. How naive I was. Android does generally work but if you have the opportunity to make a BLE app that’s iOS-only then you’ll have an awesome time.&lt;/p&gt;

&lt;p&gt;The Apple documentation &lt;a href=&quot;https://developer.apple.com/library/content/documentation/NetworkingInternetWeb/Conceptual/CoreBluetooth_concepts/PerformingCommonCentralRoleTasks/PerformingCommonCentralRoleTasks.html#//apple_ref/doc/uid/TP40013257-CH3-SW1&quot;&gt;Performing Common Central Role Tasks&lt;/a&gt; hasn’t been updated for a few years. It does have some Objective-C snippets relevant to this topic.&lt;/p&gt;

&lt;h3 id=&quot;scanning-for-advertisements&quot;&gt;Scanning for advertisements&lt;/h3&gt;

&lt;p&gt;The starting point for an iOS central is &lt;a href=&quot;https://developer.apple.com/documentation/corebluetooth/cbcentralmanager&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CBCentralManager&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://octet-stream.net/b/tdu/assets/ble/ios-c-scanning.png&quot;&gt;&lt;img src=&quot;https://octet-stream.net/b/tdu/assets/ble/ios-c-scanning.png&quot; alt=&quot;&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Initialise a new instance, passing in your own class that implements &lt;a href=&quot;https://developer.apple.com/documentation/corebluetooth/cbcentralmanagerdelegate&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CBCentralManagerDelegate&lt;/code&gt;&lt;/a&gt;.&lt;/li&gt;
  &lt;li&gt;Wait until the delegate receives &lt;a href=&quot;https://developer.apple.com/documentation/corebluetooth/cbcentralmanagerdelegate/1518888-centralmanagerdidupdatestate&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;centralManagerDidUpdateState()&lt;/code&gt;&lt;/a&gt; to confirm BLE is available.&lt;/li&gt;
  &lt;li&gt;Call &lt;a href=&quot;https://developer.apple.com/documentation/corebluetooth/cbcentralmanager/1518986-scanforperipherals&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;scanForPeripherals()&lt;/code&gt;&lt;/a&gt;, optionally filtering by the service UUID.&lt;/li&gt;
  &lt;li&gt;Wait until the delegate receives the &lt;a href=&quot;https://developer.apple.com/documentation/corebluetooth/cbcentralmanagerdelegate/1518937-centralmanager&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;didDiscover&lt;/code&gt;&lt;/a&gt; method, which includes an instance of &lt;a href=&quot;https://developer.apple.com/documentation/corebluetooth/cbperipheral&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CBPeripheral&lt;/code&gt;&lt;/a&gt;, advertisement data and an indication of signal strength. This method will be called for each peripheral that is discovered.&lt;/li&gt;
  &lt;li&gt;If you wish, establish a connection to a peripheral. Assign a delegate to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CBPeripheral&lt;/code&gt; and call &lt;a href=&quot;https://developer.apple.com/documentation/corebluetooth/cbcentralmanager/1518766-connect&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;connect()&lt;/code&gt;&lt;/a&gt; on the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CBCentralManager&lt;/code&gt;. (More about this shortly)&lt;/li&gt;
  &lt;li&gt;When finished scanning, call &lt;a href=&quot;https://developer.apple.com/documentation/corebluetooth/cbcentralmanager/1518984-stopscan&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;stopScan()&lt;/code&gt;&lt;/a&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Do pay close attention to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CBCentralManager&lt;/code&gt;’s state notifications – if it’s not &lt;a href=&quot;https://developer.apple.com/documentation/corebluetooth/cbmanagerstate&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;poweredOn&lt;/code&gt;&lt;/a&gt; (and this transition takes time) then you can’t scan. The user is also free to switch off Bluetooth at any time and the app has to deal with the consequences.&lt;/p&gt;

&lt;p&gt;When starting the scan there are a couple of important choices to make. The first is whether you want your app to get a callback for every advertisement that a peripheral transmits, or to just get notified once when a peripheral first comes into range. If you have no special need for the continuous stream of advertisements then it will be better for your users’ batteries to stick with just one callback.&lt;/p&gt;

&lt;p&gt;The second choice is whether to filter peripherals by the service UUIDs they include in their advertisements. If you are trying to connect to a particular kind of device then this is a good idea because it means you won’t be notified about every set of wireless headphones and iBeacon you come across. This simplifies your programming and again saves battery. If on the other hand you’re making a utility that scans for all BLE peripherals in range, then you would choose not to have any filtering.&lt;/p&gt;

&lt;h3 id=&quot;accessing-advertised-data&quot;&gt;Accessing advertised data&lt;/h3&gt;

&lt;p&gt;I’ve previously suggested that if an application can get away with using only broadcast advertisement data, without establishing connections, then it should. This process is fast and reliable.&lt;/p&gt;

&lt;p&gt;In &lt;a href=&quot;https://developer.apple.com/documentation/corebluetooth/cbcentralmanagerdelegate/1518937-centralmanager&quot;&gt;the delegate method where advertisements are delivered to your app&lt;/a&gt; you get a &lt;a href=&quot;https://developer.apple.com/documentation/corebluetooth/cbcentralmanagerdelegate/advertisement_data_retrieval_keys&quot;&gt;dictionary of values&lt;/a&gt; that may or may not be present depending on what the peripheral chose to include. (As far as I can tell iOS automatically performs a Scan Request/Response to capture extra data when it’s split over two advertisement packets.)&lt;/p&gt;

&lt;p&gt;If the other peripheral is an iOS app then they have have limited control over the content of the advertisements. They can set &lt;a href=&quot;https://developer.apple.com/documentation/corebluetooth/cbperipheralmanager/1393252-startadvertising&quot;&gt;only the local name and the service UUIDs&lt;/a&gt; and the name can’t be very long. The other keys may be of interest if you’re communicating with non-iOS peripherals.&lt;/p&gt;

&lt;p&gt;One more warning: Android peripherals can in certain situations transmit a &lt;em&gt;previous, out-of-date&lt;/em&gt; local name for one or two advertisements before the new name takes effect. It’s bizarre but true and this may be an issue as an iOS central if you act on the first name you see.&lt;/p&gt;

&lt;h3 id=&quot;connecting-to-a-peripheral&quot;&gt;Connecting to a peripheral&lt;/h3&gt;

&lt;p&gt;Often advertisements are not enough and we need to establish a stateful connection to the peripheral. Before you can connect to a peripheral you have must received an advertisement from it. The advertisement delegate method will receive the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CBPeripheral&lt;/code&gt; that you need – hold onto it and you can use it to make a connection either immediately or later, assuming they’re still in range.&lt;/p&gt;

&lt;p&gt;The opening and closing of connections is done by calling methods on the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CBCentralManager&lt;/code&gt;, passing the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CBPeripheral&lt;/code&gt; as a parameter. Once connected, all service and characteristic operations are initiated directly on that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CBPeripheral&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;So establishing a connection is an “outer” process that involves the manager. Since it’s the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CBCentralManager&lt;/code&gt; doing it, success or failure of that connection will go to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CBCentralManagerDelegate&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Notice that the delegate will also get a notification when we have &lt;em&gt;finished disconnecting&lt;/em&gt; from a peripheral. Don’t try connecting to that peripheral again until we get that notification. As mentioned in the last post, all BLE things take time and iOS will tell you when it’s ready.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://octet-stream.net/b/tdu/assets/ble/ios-c-connect-outer.png&quot;&gt;&lt;img src=&quot;https://octet-stream.net/b/tdu/assets/ble/ios-c-connect-outer.png&quot; alt=&quot;&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once the connection is established there is more work to do – discovery. First you must discover the service you want, then you must discover characteristics within that service to which you want to read, write or subscribe. It is necessary to go through the motions even if you’ve connected before, and even if you know exactly what kind of device it is based on the advertisement.&lt;/p&gt;

&lt;p&gt;This may seem tedious but it’s worthwhile – it gives you the opportunity to specify exactly which service UUIDs and characteristic UUIDs you want. If this is a relatively small number of characteristics it will be extremely fast, because it won’t bother retrieving anything else.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://octet-stream.net/b/tdu/assets/ble/ios-c-connect-inner.png&quot;&gt;&lt;img src=&quot;https://octet-stream.net/b/tdu/assets/ble/ios-c-connect-inner.png&quot; alt=&quot;&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once you have a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CBPeripheral&lt;/code&gt; that’s connected and has a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CBPeripheralDelegate&lt;/code&gt; configured, you can go ahead and do things like &lt;a href=&quot;https://developer.apple.com/documentation/corebluetooth/cbperipheral/1518759-readvalue&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;readValue()&lt;/code&gt;&lt;/a&gt; and &lt;a href=&quot;https://developer.apple.com/documentation/corebluetooth/cbperipheral/1518747-writevalue&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;writeValue()&lt;/code&gt;&lt;/a&gt;, passing in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CBCharacteristic&lt;/code&gt; you want to work with. Each of these operations will result in a corresponding success or failure delegate method.&lt;/p&gt;

&lt;p&gt;Unless you have a lot of idle time in your connections I would recommend only trying to do one thing at a time – i.e., connect to only one peripheral and do only one read/write before doing the next one.&lt;/p&gt;

&lt;h3 id=&quot;rangefinding-with-rssi&quot;&gt;Rangefinding with RSSI&lt;/h3&gt;

&lt;p&gt;RSSI numbers are essentially made up. The “I” stands for “indication”. No BLE devices can compensate for the physical layout of the space they’re in. There can be enormous variation in RSSI values between different devices when they are the same distance apart. The advantage of iOS is that all devices are made by the same manufacturer so there is consistency in the Bluetooth hardware and RSSI algorithms.&lt;/p&gt;

&lt;p&gt;That said, you can estimate distance approximately. Apple offers it with iBeacons – see their &lt;a href=&quot;https://developer.apple.com/documentation/corelocation/clproximity&quot;&gt;CLProximity&lt;/a&gt; enum. If &lt;em&gt;“The beacon is relatively close to the user”&lt;/em&gt; sounds vague… well, yes it is. I don’t recommend it.&lt;/p&gt;

&lt;h3 id=&quot;scanning-and-connecting-in-the-background&quot;&gt;Scanning and connecting in the background&lt;/h3&gt;

&lt;p&gt;iOS doesn’t let us have a lot of fun when our apps are in the background so BLE is a pleasant surprise. Your app can’t start scanning from the background – for example, when the user first turns on their phone. However if your app is launched and it has the right background mode, scanning will continue after the user switches to another app – even after the iPhone is locked. Just be aware that scanning happens at a much reduced rate so it will take longer to detect advertisements.&lt;/p&gt;

&lt;p&gt;What’s more, when a peripheral is detected your app’s code will be called and will have the opportunity to establish a connection and read characteristics, all without any user interaction. Depending on what you discover you might choose to launch a local notification, or simply do nothing.&lt;/p&gt;

&lt;h3 id=&quot;summary&quot;&gt;Summary&lt;/h3&gt;

&lt;p&gt;This post introduced &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CBCentralManager&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CBPeripheral&lt;/code&gt; and the things you can do with each to implement a full-featured BLE central in iOS. If you’re following along the series, you now have a pretty complete idea of how the iOS Core Bluetooth API hangs together. That means Android &lt;a href=&quot;/b/tdu/blog/2018/07/02/bluetooth-le-mobile-4-android-peripheral.html&quot;&gt;next time&lt;/a&gt;!&lt;/p&gt;
</description>
        <pubDate>Wed, 21 Mar 2018 10:15:00 +1100</pubDate>
        <link>https://octet-stream.net/b/tdu/blog/2018/03/21/bluetooth-le-mobile-3-ios-central.html</link>
        <guid isPermaLink="true">https://octet-stream.net/b/tdu/blog/2018/03/21/bluetooth-le-mobile-3-ios-central.html</guid>
        
        <category>bluetooth</category>
        
        <category>programming</category>
        
        
        <category>blog</category>
        
      </item>
    
      <item>
        <title>Bluetooth LE for Mobile - 2. iOS Peripheral</title>
        <description>&lt;p&gt;The story so far:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;a href=&quot;/b/tdu/blog/2018/02/20/bluetooth-le-mobile-1-an-overview.html&quot;&gt;An Overview&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;iOS Peripheral&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/b/tdu/blog/2018/03/21/bluetooth-le-mobile-3-ios-central.html&quot;&gt;iOS Central&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/b/tdu/blog/2018/07/02/bluetooth-le-mobile-4-android-peripheral.html&quot;&gt;Android Peripheral&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;core-bluetooth-and-ios&quot;&gt;Core Bluetooth and iOS&lt;/h3&gt;

&lt;p&gt;In the last post I described Bluetooth Low Energy (BLE) at a high level and explained roughly how it works on iOS and Android phones. Now I want to talk about the iOS API; in particular how to be a peripheral. I’m not going to go into heaps of detail – that’s what the documentation is for – but in this post I’ll introduce the key classes, describe what coding you’ll need to do, and highlight quirks that are interesting and/or dangerous.&lt;/p&gt;

&lt;p&gt;The framework for Bluetooth LE communication on iOS and macOS is called &lt;a href=&quot;https://developer.apple.com/documentation/corebluetooth&quot;&gt;Core Bluetooth&lt;/a&gt;. Despite the broad name it is almost entirely for GATT communication, which is all about the &lt;em&gt;services&lt;/em&gt; and &lt;em&gt;characteristics&lt;/em&gt; mentioned previously.&lt;/p&gt;

&lt;h3 id=&quot;prerequisites&quot;&gt;Prerequisites&lt;/h3&gt;

&lt;p&gt;Your device must have Bluetooth turned on. Obviously. If you want your app to be able to act as a Central or Peripheral when it is not in the foreground you must enable the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bluetooth-central&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bluetooth-peripheral&lt;/code&gt; Background Modes in Xcode.&lt;/p&gt;

&lt;h3 id=&quot;being-a-peripheral&quot;&gt;Being a Peripheral&lt;/h3&gt;

&lt;p&gt;To turn your iPhone or iPad or Mac into a Bluetooth LE peripheral you need an instance of &lt;a href=&quot;https://developer.apple.com/documentation/corebluetooth/cbperipheralmanager&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CBPeripheralManager&lt;/code&gt;&lt;/a&gt;. You will need to provide it a delegate of type &lt;a href=&quot;https://developer.apple.com/documentation/corebluetooth/cbperipheralmanagerdelegate&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CBPeripheralManagerDelegate&lt;/code&gt;&lt;/a&gt;. Implement this in your class and pass it in when you initialise the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CBPeripheralManager&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The required steps are described pretty well in the Apple documentation &lt;a href=&quot;https://developer.apple.com/library/content/documentation/NetworkingInternetWeb/Conceptual/CoreBluetooth_concepts/PerformingCommonPeripheralRoleTasks/PerformingCommonPeripheralRoleTasks.html&quot;&gt;&lt;em&gt;Performing Common Peripheral Role Tasks&lt;/em&gt;&lt;/a&gt; (albeit in Objective-C) so I won’t bore you with code snippets. Here’s a diagram though.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://octet-stream.net/b/tdu/assets/ble/peripheral.png&quot;&gt;&lt;img src=&quot;https://octet-stream.net/b/tdu/assets/ble/peripheral.png&quot; alt=&quot;&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;First you need to allocate UUIDs for your services and characteristics. To create a &lt;a href=&quot;https://developer.apple.com/documentation/corebluetooth/cbmutablecharacteristic&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CBMutableCharacteristic&lt;/code&gt;&lt;/a&gt; you need to specify its UUID and its permissions such as whether it is readable or writable. Then you create a &lt;a href=&quot;https://developer.apple.com/documentation/corebluetooth/cbmutableservice&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CBMutableService&lt;/code&gt;&lt;/a&gt; and assign an array of characteristics to it. If you’re unsure you probably want to create a &lt;em&gt;primary&lt;/em&gt; service.&lt;/p&gt;

&lt;p&gt;Once you’ve built up a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CBMutableService&lt;/code&gt;, add it to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CBPeripheralManager&lt;/code&gt; with &lt;a href=&quot;https://developer.apple.com/documentation/corebluetooth/cbperipheralmanager/1393255-add&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;add()&lt;/code&gt;&lt;/a&gt;. Finally, when all your services are added, call &lt;a href=&quot;https://developer.apple.com/documentation/corebluetooth/cbperipheralmanager/1393252-startadvertising&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;startAdvertising()&lt;/code&gt;&lt;/a&gt; on that manager, providing the device name and the array of advertised service UUIDs. Your delegate will be called with &lt;a href=&quot;https://developer.apple.com/documentation/corebluetooth/cbperipheralmanagerdelegate/1393321-peripheralmanagerdidstartadverti&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;peripheralManagerDidStartAdvertising()&lt;/code&gt;&lt;/a&gt;. If the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;error&lt;/code&gt; parameter is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nil&lt;/code&gt;, congratulations, your peripheral is now live.&lt;/p&gt;

&lt;h3 id=&quot;tip-its-all-async&quot;&gt;Tip: It’s all async&lt;/h3&gt;

&lt;p&gt;When using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CBPeripheralManager&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CBCentralManager&lt;/code&gt; look closely at their respective delegate methods, implement all of them, and understand when they are called and why.&lt;/p&gt;

&lt;p&gt;Error handling is critical because in BLE errors happen all the damn time. It may be due to lack of permissions, Bluetooth being switched off, or congestion on 2.4 GHz. It is way less reliable than just about any other API. It’s not Apple’s fault; it’s just the nature of things and if an app doesn’t pay attention and manage its state properly it will have a bad time.&lt;/p&gt;

&lt;p&gt;Virtually every action that you can take with the BLE API takes a certain amount of time to complete and it will tell the delegate when it succeeds or fails. It’s important to wait for these notifications before doing the next thing. For example, disconnecting from a device takes time. If you attempt to reconnect before receiving didDisconnectPeripheral then it will not end well.&lt;/p&gt;

&lt;p&gt;It is somewhat unfortunate that iOS wound up with a “massive delegate” design for this API. There’s an argument to be made for farming off particular tasks and responsibilities to multiple smaller classes. You’re forced into writing a big stateful class that can handle an arbitrary number of characteristics for an arbitrary number of connecting peripherals. Recognise that this is going to happen and do your best to move complexity out to helper classes.&lt;/p&gt;

&lt;h3 id=&quot;handling-read-and-write-requests&quot;&gt;Handling read and write requests&lt;/h3&gt;

&lt;p&gt;Surprisingly, if you are an iOS peripheral the operating system will not tell you when a central connects or disconnects. It just isn’t part of the API. So your app will be advertising merrily away and suddenly it will be interrupted by a read or write request. (It could be also be a subscription event – more about those shortly.)&lt;/p&gt;

&lt;p&gt;There are two delegate methods for responding to read and write requests. For a read you must provide a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Data&lt;/code&gt; (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;NSData&lt;/code&gt;) containing the bytes to return. In a write request are provided with a &lt;a href=&quot;https://developer.apple.com/documentation/corebluetooth/cbattrequest&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CBAttRequest&lt;/code&gt;&lt;/a&gt; that contains the bytes that were written by the central.&lt;/p&gt;

&lt;p&gt;In both cases the delegate is given two pieces of information – a &lt;a href=&quot;https://developer.apple.com/documentation/corebluetooth/cbcentral&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CBCentral&lt;/code&gt;&lt;/a&gt;, who has a UUID identifier that tracks a particular device for the duration of its connection, and the &lt;a href=&quot;https://developer.apple.com/documentation/corebluetooth/cbcharacteristic&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CBCharacteristic&lt;/code&gt;&lt;/a&gt; that was targeted, which contains the UUID you assigned to it earlier on.&lt;/p&gt;

&lt;p&gt;There is no rule that you have to give every central the same value for a particular characteristic. There is no rule that you have to provide the same value every time a central reads a given characteristic. Let your imagination run wild – but realise that if you rely on a series of reads or writes, the longer your process goes, the lower the probability that it will fully succeed.&lt;/p&gt;

&lt;p&gt;However there are two important things that the iOS peripheral does not know:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;If a central reads a value and you provide it, you don’t know for sure if they ever got it.&lt;/li&gt;
  &lt;li&gt;If a central writes a value and you use it, you don’t know for sure they got the acknowledgement that the write was successful.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is not especially limiting but it’s something to keep in mind if you want your app to be fault tolerant.&lt;/p&gt;

&lt;h3 id=&quot;the-trouble-with-subscriptions&quot;&gt;The trouble with subscriptions&lt;/h3&gt;

&lt;p&gt;Have you ever written an app that gummed up iOS so badly that the entire device needs a reboot to start working again? I’ve done it only once and it was using BLE subscriptions.&lt;/p&gt;

&lt;p&gt;A connected central can choose to subscribe to a characteristic. Your app will receive delegate calls when they subscribe and unsubscribe. When the characteristic notionally “changes value”, or perhaps you just want to send a bunch of chunks of data in a row, you can invoke &lt;a href=&quot;https://developer.apple.com/documentation/corebluetooth/cbperipheralmanager/1393281-updatevalue&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;updateValue()&lt;/code&gt;&lt;/a&gt; to tell iOS that it should transmit a new value to the relevant subscribers.&lt;/p&gt;

&lt;p&gt;But sometimes &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;updateValue()&lt;/code&gt; returns &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;false&lt;/code&gt;, indicating failure. Have a look at &lt;a href=&quot;https://developer.apple.com/documentation/corebluetooth/cbperipheralmanagerdelegate/1393248-peripheralmanagerisready&quot;&gt;this crazy method&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You have to wait for this mysterious queue to empty out a bit, at which point you receive this call on your delegate. You can then do more &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;updateValue()&lt;/code&gt;s until one of them returns &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;false&lt;/code&gt;. Then you have to wait again.&lt;/p&gt;

&lt;p&gt;Okay so this is kind of clunky and state-heavy but you can work with it to get the data through – unless the connection to the device fails abruptly. In that case iOS sometimes gets stuck trying to communicate with devices that don’t exist, the queue never empties, and it all goes to hell in a handbasket.&lt;/p&gt;

&lt;p&gt;I should mention that I did most of this testing on iOS 9 which is now a little dated. All the same I would recommend avoiding the subscription API unless you specifically need sporadic realtime updates from peripheral to central – i.e., exactly what it was designed for.&lt;/p&gt;

&lt;p&gt;For everything else, such as sending larger chunks of data, you are far better off to have the central do just one read or write at a time, wait for the success result, then do the next one. This way you never pile up on any queues and failures can be handled far more gracefully.&lt;/p&gt;

&lt;h3 id=&quot;big-data-with-ios-ble&quot;&gt;Big Data with iOS BLE&lt;/h3&gt;

&lt;p&gt;Under normal circumstances BLE characteristic values are limited to 20 bytes in length. When the central and peripheral are both running iOS the rules can be bent a little.&lt;/p&gt;

&lt;p&gt;CBCentral has a &lt;a href=&quot;https://developer.apple.com/documentation/corebluetooth/cbcentral/1408800-maximumupdatevaluelength&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;maximumValueLength&lt;/code&gt;&lt;/a&gt; parameter which indicates how many bytes you’re allowed to send it at once. I have seen iPad centrals reporting values of around 160 bytes. I don’t have measurements but this may speed things up for you if you’re trying to send a lot of data via characteristic reads.&lt;/p&gt;

&lt;h3 id=&quot;next-steps&quot;&gt;Next steps&lt;/h3&gt;

&lt;p&gt;In the next post I will describe being a BLE central in iOS. Originally I was going to cover all of iOS in this post but it was getting way too long.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/b/tdu/blog/2018/03/21/bluetooth-le-mobile-3-ios-central.html&quot;&gt;Part 3: iOS Central&lt;/a&gt;&lt;/p&gt;
</description>
        <pubDate>Thu, 01 Mar 2018 21:00:00 +1100</pubDate>
        <link>https://octet-stream.net/b/tdu/blog/2018/03/01/bluetooth-le-mobile-2-ios-peripheral.html</link>
        <guid isPermaLink="true">https://octet-stream.net/b/tdu/blog/2018/03/01/bluetooth-le-mobile-2-ios-peripheral.html</guid>
        
        <category>bluetooth</category>
        
        <category>programming</category>
        
        
        <category>blog</category>
        
      </item>
    
      <item>
        <title>Bluetooth LE for Mobile - 1. An Overview</title>
        <description>&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;An Overview&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/b/tdu/blog/2018/03/01/bluetooth-le-mobile-2-ios-peripheral.html&quot;&gt;iOS Peripheral&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/b/tdu/blog/2018/03/21/bluetooth-le-mobile-3-ios-central.html&quot;&gt;iOS Central&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/b/tdu/blog/2018/07/02/bluetooth-le-mobile-4-android-peripheral.html&quot;&gt;Android Peripheral&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;introduction&quot;&gt;Introduction&lt;/h3&gt;

&lt;p&gt;This is the first in a series of posts introducing Bluetooth Low Energy (BLE). The target audience is iOS and Android mobile developers who want to use BLE to communicate with external hardware, or with another device running their app.&lt;/p&gt;

&lt;p&gt;In the last couple of years I’ve spent a bunch of time building cross-platform apps that use Bluetooth LE – in other words, using CoreBluetooth and the Android Bluetooth API in apps that can reliably advertise, scan, and transfer different kinds of data to each other.&lt;/p&gt;

&lt;p&gt;In this introduction I want to bring the main features together in some detail and describe how to use them on both platforms. Basically it’s the things I wish I knew when I started out with cross-platform BLE.&lt;/p&gt;

&lt;p&gt;I should point out up front that I am not an expert on the Bluetooth protocol, the hardware or the RF behaviour – I haven’t even read much of the spec. I’m just a guy who spent a long time banging his head against the APIs that these platforms provide.&lt;/p&gt;

&lt;p&gt;This first post is a high level overview of what it’s like to use BLE, what the words mean and what it has to offer your app.&lt;/p&gt;

&lt;h3 id=&quot;a-cross-platform-radio-protocol&quot;&gt;A Cross-Platform Radio Protocol&lt;/h3&gt;

&lt;p&gt;Bluetooth Low Energy lets modern iOS and Android devices and various gadgets communicate without needing WiFi or Internet access. It’s an industry standard and all supported devices are compatible with each other. It works reliably over a range of tens of metres.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://octet-stream.net/b/tdu/assets/ble/process.png&quot;&gt;&lt;img src=&quot;https://octet-stream.net/b/tdu/assets/ble/process.png&quot; alt=&quot;&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It’s effectively a client/server protocol. A &lt;em&gt;peripheral&lt;/em&gt; is the server – it announces itself by transmitting a regular stream of advertisements. These advertisements are received by a client, which is called a &lt;em&gt;central&lt;/em&gt;. The central may choose to establish a temporary connection to the peripheral. Using that connection the central can request to read or write data. The peripheral plays a generally passive role and responds to requests from the central.&lt;/p&gt;

&lt;p&gt;When you think about Bluetooth gadgets in the real world – smart light bulbs, iBeacons, and so on – these are all peripherals. They advertise continuously and your phone acts as a central, detecting them when they’re in range, pulling data off them and letting the user control them.&lt;/p&gt;

&lt;p&gt;Once granted the right app-level permissions, iOS and Android devices can scan and connect to arbitrary peripherals without requiring any user interaction. This is very different from ordinary Bluetooth where there is a heavyweight pairing procedure.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://octet-stream.net/b/tdu/assets/ble/datastore.png&quot;&gt;&lt;img src=&quot;https://octet-stream.net/b/tdu/assets/ble/datastore.png&quot; alt=&quot;&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Conceptually the peripheral is a simple key/value datastore. It hosts &lt;em&gt;services&lt;/em&gt;, each of which is a list of one or more &lt;em&gt;characteristics&lt;/em&gt;. Each characteristic is a chunk of binary data up to 20 bytes (normally). Every service and characteristic is identified by its own UUID. When a central is reading or writing data, it is doing it with a particular characteristic.&lt;/p&gt;

&lt;p&gt;When an iOS or Android device acts a peripheral – advertising, and accepting connections from other devices – it generally supports connections from multiple centrals simultaneously. The app developer may choose to present the same or different data to each central.&lt;/p&gt;

&lt;h3 id=&quot;advertising-and-scanning&quot;&gt;Advertising and Scanning&lt;/h3&gt;

&lt;p&gt;The first step in any BLE communication is to have a peripheral broadcast an advertisement. This is a packet that is repeatedly transmitted typically around 10 or 20 times per second. These packets will normally include two pieces of information: a service UUID and a device name.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://octet-stream.net/b/tdu/assets/ble/advertisingpackets.png&quot;&gt;&lt;img src=&quot;https://octet-stream.net/b/tdu/assets/ble/advertisingpackets.png&quot; alt=&quot;&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The service UUID is how you identify a particular type of device or a particular app. A peripheral may host multiple services but it should pick at least one to mention in its advertisement.&lt;/p&gt;

&lt;p&gt;Imagine you’re writing an app that talks to your brand of smart light bulb. You want your app to only detect your light bulbs, not every single BLE device in range. The light bulb will advertise a service UUID that is unique. When you scan in your app you can specify this target UUID and the OS will only bother to tell you when it hears a device that matches.&lt;/p&gt;

&lt;p&gt;The device name is just a string and normally human readable. If you have multiple smart light bulbs you might give them different names like “Lounge”, “Kitchen”, and so on. This way you can identify a specific device by its advertisement data alone.&lt;/p&gt;

&lt;p&gt;On the central side, your app can choose to be be notified either every time it hears a matching advertisement, or just once per device that comes into range.&lt;/p&gt;

&lt;h3 id=&quot;power-saving-and-ble-performance&quot;&gt;Power Saving and BLE Performance&lt;/h3&gt;

&lt;p&gt;Even though it’s called Bluetooth &lt;em&gt;Low Energy&lt;/em&gt;, if you’re advertising or scanning all the time this will use an appreciable amount of energy from your phone battery. As a result operating systems will pretend to do what you asked but don’t guarantee that they will run the radio all the time. iOS is particularly tough about this when your app goes into the background and when the phone is locked.&lt;/p&gt;

&lt;p&gt;The exact algorithms are not public but anecdotally it seems that when the OS wants to save power it operates the BLE radio for brief spurts at a time. The theory goes that if some device out there is transmitting an advertisement 10 times per second, if you listen for 100 ms out of every second (10% of the time), you will probably notice that device within one second and this is fast enough.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://octet-stream.net/b/tdu/assets/ble/centrallistening.png&quot;&gt;&lt;img src=&quot;https://octet-stream.net/b/tdu/assets/ble/centrallistening.png&quot; alt=&quot;&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Ultimately there are trade-offs between power and performance. Android gives the programmer slightly more control over this than iOS. If you are building a device that needs to be detected by locked iPhones it is important that the advertising rate is nice and high.&lt;/p&gt;

&lt;h3 id=&quot;establishing-connections&quot;&gt;Establishing Connections&lt;/h3&gt;

&lt;p&gt;First let us be clear: BLE does not offer much bandwidth. It is not designed for streaming data. If you do it anyway, speeds are around a few hundred bytes per second under good conditions. (It totally does work though.)&lt;/p&gt;

&lt;p&gt;A central that has detected a peripheral by its advertisements may or may not want to connect. In some cases the information contained in the advertisement along with the received signal strength (RSSI) may be all its needs to do its job. Compared with everything else in mobile BLE, transmitting and receiving advertisements is super reliable. If this is enough information to get the job done then I’d highly recommend not even bothering with connections. Unfortunately that’s pretty rare and you will normally need to connect to read data of your choice, or to write any data at all.&lt;/p&gt;

&lt;p&gt;From the API’s point of view there are several phases:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Establishing an initial connection&lt;/li&gt;
  &lt;li&gt;Discovering the services that are available&lt;/li&gt;
  &lt;li&gt;Discovering the characteristics that are available for a given service&lt;/li&gt;
  &lt;li&gt;Finally, using those discovered characteristics to read and write data&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Even when you &lt;em&gt;know&lt;/em&gt; that a particular device is offering certain services and characteristics you still have to go through the rigmarole of discovering them every time you connect.&lt;/p&gt;

&lt;p&gt;In Android steps 2 and 3 are handled by a single request that goes ahead and discovers every service and characteristic available on a given device. This is easy to use but the downside is that it’s very slow. On Android 5 and below it takes around 6 or 7 seconds to interrogate a typical iOS device. This was substantially sped up in Android 6+ but it’s still slow. Caching does help if you make multiple connections to the same device.&lt;/p&gt;

&lt;p&gt;In iOS there are two distinct stages. First you discover services, which you can specify by UUID, then you discover characteristics, which you can also specify by UUID. iOS connects really dang fast.&lt;/p&gt;

&lt;h3 id=&quot;reading-and-writing-data&quot;&gt;Reading and Writing Data&lt;/h3&gt;

&lt;p&gt;Both iOS and Android offer a lot of control over the reading and writing of data.&lt;/p&gt;

&lt;p&gt;Centrals can:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Initiate a read request or a write request&lt;/li&gt;
  &lt;li&gt;Get a callback indicating whether a given read or write request succeeded or failed&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Peripherals can:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Respond to each read and write request individually, knowing the identity of the central that requested it&lt;/li&gt;
  &lt;li&gt;Choose which data to send dynamically in response to each read request&lt;/li&gt;
  &lt;li&gt;Choose what to do with the data received in each write request&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With this level of control it is straightforward to build up interesting application level protocols using these operations as building blocks.&lt;/p&gt;

&lt;p&gt;It is also possible for a connected central to subscribe or unsubscribe to a particular characteristic. When a central has subscribed, the peripheral can transmit updates to this characteristic and as long as the central remains connected they will receive those updates asynchronously.&lt;/p&gt;

&lt;p&gt;You might assume (as one Apple sample project does) that this is a great way to create a data channel from peripheral to central. In practice the iOS subscription API has some major issues. In a later post about iOS I will explain why I think it’s silly.&lt;/p&gt;

&lt;h3 id=&quot;gatt&quot;&gt;GATT&lt;/h3&gt;

&lt;p&gt;I should mention that a lot of what I am describing in this post is technically GATT, which is just one part of Bluetooth Low Energy. GATT is basically all you get in iOS and Android so all the documentation just calls it “Bluetooth LE”. I won’t try to explain any more because as I mentioned at the start, I only know about what the APIs provide.&lt;/p&gt;

&lt;h3 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h3&gt;

&lt;p&gt;In this post I’ve introduced the main concepts of Bluetooth Low Energy – what you can use it for, the general lifecycle of the API, and the main terms such as &lt;em&gt;peripheral&lt;/em&gt;, &lt;em&gt;central&lt;/em&gt;, &lt;em&gt;service&lt;/em&gt; and &lt;em&gt;characteristic&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Even though they provide almost the same functionality, the iOS and Android APIs wound up being fairly different. In the next few posts I’m going to briefly describe the architecture of each one and highlight some of the interesting parts.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/b/tdu/blog/2018/03/01/bluetooth-le-mobile-2-ios-peripheral.html&quot;&gt;Part 2: iOS Peripheral&lt;/a&gt;&lt;/p&gt;
</description>
        <pubDate>Tue, 20 Feb 2018 22:00:00 +1100</pubDate>
        <link>https://octet-stream.net/b/tdu/blog/2018/02/20/bluetooth-le-mobile-1-an-overview.html</link>
        <guid isPermaLink="true">https://octet-stream.net/b/tdu/blog/2018/02/20/bluetooth-le-mobile-1-an-overview.html</guid>
        
        <category>bluetooth</category>
        
        <category>programming</category>
        
        
        <category>blog</category>
        
      </item>
    
  </channel>
</rss>
