Engineering · · 8 min

How to build a BLE companion app: lessons from a 20-electrode EMS suit

Pairing, background connections, command protocols and safety limits: what it takes to ship a consumer app that controls Bluetooth hardware, from the OHM Fitness app.

Listen to the summary ·

Read the transcript

The OHM at Home app has 20 electrodes on the other end of its Bluetooth connection, so a dropped packet is a workout that stops halfway, on a person. We built it, native on iOS and Android, so OHM Fitness members can run guided 25-minute EMS sessions at home, without a trainer in the room.

Four lessons came out of that build. Pairing must be one tap, with clear recovery when it fails. Every control command needs an acknowledgement and a timeout. Safety limits live in both the firmware and the app. And testing happens on the phones your users own, not on an emulator.

Both platforms now offer pairing built for companion apps. On iOS 18 and later, AccessorySetupKit pairs in one tap, and Apple says apps that declare it don’t show the Bluetooth permission prompt. On Android 8.0 and later, companion device pairing lets the system scan on the app’s behalf.

Agree the command protocol in writing with the firmware team. Use absolute commands: set zone 3 to level 12 survives a retry, while increase by one, applied twice, is a different session. After a reconnect, read the device’s state before sending anything.

Skip the custom app when the device maker’s own app or SDK already does the job.

The guide below ends with a 10-point checklist to work through before you start.

AI-generated voice summary of this guide.

A companion app has 20 electrodes on the other end of its Bluetooth connection, and a dropped packet is not a spinner: it is a workout that stops halfway, on a person.

OHM Fitness ran its EMS training only in studios. To grow beyond them, OHM needed an app members could use at home to drive the emPower Suit, a wearable with 20 electrodes, safely and without a trainer in the room. We built that app, native on iOS and Android. This guide is what we would tell any team starting a BLE companion app, and it pairs with our IoT and BLE app development page, which covers the device-to-cloud side.

What we learned building OHM at Home

  • Pairing must be one tap, with clear recovery when it fails.
  • Control commands need acknowledgements and timeouts, never fire-and-forget.
  • Safety limits live in both firmware and app, never just one.
  • Test on the phones your users own, not on an emulator.

The rest of this guide expands each lesson with the platform rules that apply in 2026, checked against Apple and Google documentation on October 18, 2026.

Pairing people can recover from

Pairing is the first experience every user has, and the first place a connected product can fail. One tap is the goal, but recovery matters more: Bluetooth switched off, a permission denied, a suit that is not charged, or a phone that remembers an old connection. Each needs a plain message and a single next step, not a generic error.

Both platforms now offer a pairing flow designed for companion apps, and it is worth using them on a new build:

  • iOS 18 and later: AccessorySetupKit. The app hands the system a description of its accessory, and iOS shows a picker with the product name and image. One tap pairs it and grants the app access to that accessory. Apple’s WWDC24 session says apps that declare AccessorySetupKit support do not show the Bluetooth permission prompt at all. Accessories that need stronger security still show pairing UI such as numeric comparison or a passkey.
  • Android 8.0 and later: companion device pairing. The system scans on the app’s behalf, without the app holding ACCESS_FINE_LOCATION, although Location Services must be switched on. After the user associates a device, the app can request permissions to run and use data in the background and to start a foreground service from the background.

On Android 12 and later, an app that scans and connects on its own must declare and request BLUETOOTH_SCAN and BLUETOOTH_CONNECT. Design the permission screens early; a denied permission is a pairing failure the user caused without knowing it.

Companion pairing flows on each platform
iOS: AccessorySetupKitiOS 18 and later
  • A system picker with the product name and image
  • One tap pairs and grants access to that accessory
  • No Bluetooth permission prompt
Android: companion device pairingAndroid 8.0 and later
  • The system scans on the app’s behalf
  • No ACCESS_FINE_LOCATION, but Location Services on
  • Background permissions and a foreground service after association

Keeping the connection alive in the background

A BLE app can keep working when the phone locks, but the platforms limit how, and the product has to be designed around those limits. Decide in discovery what the device does when the app goes to the background mid-session, because that answer shapes both the firmware and the app.

On iOS, the bluetooth-central background mode lets an app keep discovering, connecting to and exchanging data with peripherals while it is in the background, and the system wakes the app when a Bluetooth event arrives. Three limits matter:

  • Scanning slows down. Background scans coalesce repeated advertisements into one event and scan less often, so finding a device can take longer.
  • Wake-ups are short. Apple’s guide says an app has around 10 seconds to complete a task after it is woken, so keep that work small.
  • The app can be terminated. With state preservation and restoration, iOS remembers the peripherals the app was connected to and the characteristics it subscribed to, and relaunches the app with that state when something happens.

On Android, background work for an associated device runs through the companion permissions above and a foreground service, which shows a persistent notification while the session runs.

Real-time control needs acknowledgements

A command to change intensity is not done when the app sends it; it is done when the device confirms it. Every command should expect an acknowledgement and have a timeout, and the interface should show what the device reports, not what the app requested. When a connection drops mid-session, the app has to decide quickly and predictably what happens next, and the device has to be safe even if the app never reconnects.

Sequence between app and device. Normal: the app sends set zone 3 to level 12, the device replies with an ack that zone 3 is at level 12, and the screen shows what the device reports. Connection drops: the same command gets no reply before the timeout, which counts as a failure, and on reconnect the app reads the device state first.Sequence between app and device. Normal: the app sends set zone 3 to level 12, the device replies with an ack that zone 3 is at level 12, and the screen shows what the device reports. Connection drops: the same command gets no reply before the timeout, which counts as a failure, and on reconnect the app reads the device state first.
Absolute commands, acknowledged, with a timeout and a state read-back after reconnect.

Designing the command protocol

Write the protocol down and agree it with the firmware team before app work starts. A protocol that only exists in chat messages gets implemented two different ways, once in firmware and once in the app.

A protocol that holds up in production has these properties:

  • Versioned. The device reports its protocol version on connect, and the app refuses or adapts to versions it does not know. Firmware updates will happen; the app must not guess.
  • Absolute, not relative. “Set zone 3 to level 12” survives a retry; “increase zone 3 by one” applied twice is a different session.
  • Acknowledged. Every write gets a reply with the resulting state, and the app treats a missing reply as a failure after a fixed timeout.
  • Recoverable. After a reconnect, the app reads the device’s current state before sending anything, instead of assuming nothing changed.
  • Small. Bluetooth Low Energy packets are small, so compact binary messages with a documented layout beat JSON on the wire.

The document should also list every error the device can return and what the app shows for each one.

Safety limits live in two places

With wearable hardware, the firmware is the last line of defense: it enforces hard limits whatever the app sends. The app enforces the same limits in the interface, adds session rules and guidance, and never offers a setting the device would refuse. OHM’s app carries those rules into each workout as session limits and intensity guardrails.

Security belongs in the same conversation. Android’s documentation warns that once devices pair over Bluetooth Low Energy, the data they exchange is accessible to all apps on the phone, so sensitive data needs its own app-layer encryption. NIST IR 8259A gives a voluntary baseline for the device side, with six capabilities: device identification, device configuration, data protection, logical access to interfaces, software update and cybersecurity state awareness. Its software update capability, updates only by authorized entities through a secure mechanism, is the one a companion app most often delivers, because the app is usually how firmware reaches the device.

Test on real devices

Emulators cannot test Bluetooth properly, so real phones and real hardware are where a companion app is signed off. Build a device lab covering the phone models and operating system versions in your user base, and run the same test plan on each:

  • First pairing, re-pairing after a reset and pairing with a second phone.
  • Permission denied, Bluetooth off and Location Services off on Android.
  • Connection loss mid-command, and reconnection with state read-back.
  • Background and lock-screen behavior for a full session length.
  • Long sessions on a low battery, for both the phone and the device.
  • Upgrades: the app over an old firmware version, and an OS update on the phone.

Before the hardware is ready, a simulated peripheral that implements the agreed protocol keeps app work moving. It does not replace the real device for timing and radio behavior.

“With hardware, reliability is the user experience.”

What a hardware companion app costs

A companion app is priced like any other build on our pricing page: a $5,000 Discovery Sprint to settle the protocol, the platforms and the riskiest flow on real hardware, then a fixed price inside the $25k–$120k band. The device layer is what moves it within that band:

  • Firmware maturity. A documented, stable protocol is the biggest saving. A protocol still changing every week makes the app chase it.
  • Device count. Each model or hardware revision adds pairing rules, commands and test passes.
  • Real-device testing time. Long sessions, background behavior and OS upgrades take calendar time, not only engineering time.
  • Cloud and data. Syncing sessions, telemetry or memberships to a backend adds a service to build and run; that part is often a cloud architecture job of its own.

Where a given app lands in the band is our judgment after discovery, not a figure we can promise before seeing the protocol.

When you don’t need a custom app

Skip the custom app when someone else’s software already does the job. The device maker’s own app or white-label SDK is often enough for a pilot or a small fleet. If devices already report to a hub or gateway, a web dashboard over that data may be all users need, with no phone in the control path.

And if the firmware is still changing every week, stabilize the protocol first. An app built against a moving protocol is rebuilt, not finished.

A checklist before you start

  1. A written command protocol, versioned and agreed with the firmware team.
  2. A decision on native or cross-platform for the Bluetooth layer.
  3. Pairing flows chosen per platform, including AccessorySetupKit and companion device pairing where they fit.
  4. Background behavior defined for a locked phone and a lost connection.
  5. Safety limits listed, with which ones the firmware enforces.
  6. App-layer encryption for any sensitive data over BLE.
  7. A firmware update path, and who signs the updates.
  8. A simulated peripheral for development.
  9. A device lab that matches your users’ phones.
  10. A test plan that covers the six cases above on every release.

Today OHM at Home runs guided sessions in four training modes for members on both app stores, and it gave OHM a remote membership it could not sell before. If you are building a companion app for connected hardware, our mobile app development team can help, and our guide to native or cross-platform covers the framework decision.

Related service

Native and cross-platform iOS and Android apps, from fintech and streaming to connected hardware, designed, built and launched to the stores.

Want this applied to your business?
Free 45-min AI audit with a senior architect.
Book the audit
FAQ

Common questions.

Should a hardware companion app be native or cross-platform?

Either can work, but the device connection needs well-tested platform code. For OHM at Home we built native iOS and Android apps, because real-time control of the suit is the core of the product and has to behave exactly right on each platform.

How do you make Bluetooth pairing reliable?

Make it one tap, show the user what is happening at each step, and design the recovery path as carefully as the happy path: retry, re-scan, and plain instructions when Bluetooth is off or a permission is missing.

Where should safety limits for a connected device live?

In both the firmware and the app. The firmware enforces hard limits even if the app misbehaves or the connection drops, and the app enforces the same limits in the interface so users never request something the device will refuse.

Do iOS users have to grant Bluetooth permission to pair an accessory?

Not when the app uses AccessorySetupKit on iOS 18 or later. Apple’s WWDC24 session says apps that declare AccessorySetupKit support do not trigger the Bluetooth permission prompt; the user picks the accessory in a system sheet instead. Apps that use the older Core Bluetooth flow still show the prompt.

Can a BLE app keep working when the phone is locked?

Yes, within limits. On iOS the bluetooth-central background mode lets an app stay connected and exchange data in the background, but background scanning is slower and each wake-up is short. On Android, an app associated through companion device pairing can request permissions to run and start a foreground service from the background.

How do you test a BLE app before the hardware is ready?

Start against a simulated peripheral that implements the agreed protocol, such as a second phone or a development board advertising the same services, so app work is not blocked. Sign-off still happens on production hardware, because timing, power and radio behavior only show up on the real device.

How much does a BLE companion app cost?

It is quoted as a fixed price after a short discovery phase. A documented, stable firmware protocol keeps it lower; a protocol that is still changing, several device models and long real-device test cycles push it up. Our pricing page lists the bands.