---
title: "How to Build a Connected Device Companion App With BLE and IoT"
author: "Nate Laquis"
author_role: "Founder & CEO"
date: "2029-03-25"
category: "How to Build"
tags:
  - connected device companion app BLE IoT development
  - BLE mobile app development
  - IoT companion app architecture
  - Bluetooth Low Energy device pairing
  - connected hardware mobile integration
excerpt: "Building a companion app for a physical device is one of the most technically demanding mobile projects you can take on. Here is what actually matters, from BLE pairing to firmware updates and backend sync."
reading_time: "15 min read"
canonical_url: "https://kanopylabs.com/blog/how-to-build-a-connected-device-companion-app"
---

# How to Build a Connected Device Companion App With BLE and IoT

## Why Companion Apps Are Harder Than Regular Mobile Apps

A companion app is the software bridge between a physical device and the person using it. Think Oura Ring, Peloton, Ring Doorbell, or any Bluetooth-connected medical device. The app handles setup, data display, firmware updates, notifications, and often serves as the primary control interface for the hardware itself.

What makes these projects uniquely difficult is the dependency chain. A standard SaaS mobile app talks to your API over HTTPS, and that is about it. A companion app must negotiate a Bluetooth Low Energy connection, handle pairing and bonding across iOS and Android (which behave very differently), manage intermittent connectivity, buffer data locally when the device is out of range, sync that data to a cloud backend when connectivity returns, and push firmware updates over the air without bricking the device. Each of those steps introduces failure modes that pure software apps never encounter.

The timeline reflects this complexity. A well-scoped companion app for a single BLE peripheral takes 4 to 6 months for a skilled team of 3 to 4 engineers. If you need both iOS and Android from day one, add another 2 months or budget for a cross-platform framework like React Native or Flutter with native BLE bridges. Total development cost typically lands between $150,000 and $350,000 for an MVP, depending on the number of device features exposed through the app.

This guide walks through every layer of the stack: BLE communication, device provisioning, data architecture, firmware OTA, backend design, and testing strategies. If you are building hardware and need a mobile experience to go with it, this is the playbook.

![Multiple mobile devices displaying connected IoT companion app interfaces](https://images.unsplash.com/photo-1512941937669-90a1b58e7e9c?w=800&q=80)

## BLE Architecture: How Your App Talks to Hardware

Bluetooth Low Energy is the default protocol for companion apps that communicate with nearby devices. BLE 5.3 is the current standard, offering data rates up to 2 Mbps, range up to 200 meters (line of sight), and power consumption low enough for coin cell batteries. Understanding how BLE works at the protocol level is essential for building a reliable companion app.

### GATT Profiles, Services, and Characteristics

BLE communication is structured around the Generic Attribute Profile (GATT). Your device firmware exposes one or more "services," each containing "characteristics." A fitness tracker might expose a Heart Rate Service with characteristics for heart rate measurement, sensor location, and control point. Each characteristic has properties: read, write, notify, or indicate. Your companion app discovers these services during connection and subscribes to the ones it needs.

Design your GATT profile carefully. Every characteristic you add increases connection overhead and complicates the firmware. Start with the minimum viable set: a data streaming characteristic (notify), a command characteristic (write), and a device info service. You can add more in later firmware versions.

### iOS vs Android BLE: The Platform Gap

This is where companion app development gets painful. Apple's CoreBluetooth framework and Android's BluetoothGatt API have fundamentally different behaviors.

- **MTU negotiation:** iOS automatically negotiates the maximum transmission unit (up to 512 bytes). Android requires you to call requestMtu() explicitly after connection, and some devices reject the request silently.

- **Connection intervals:** iOS enforces strict connection parameter guidelines. If your firmware requests intervals outside Apple's allowed range (15ms to 2 seconds), the connection will be dropped without explanation. Android is more lenient but less predictable.

- **Background scanning:** iOS severely restricts BLE scanning in the background. You must specify exact service UUIDs to scan for, and scan events are batched. Android allows broader background scanning but requires a foreground service on Android 12+.

- **Bonding behavior:** iOS handles bonding automatically when an encrypted characteristic is accessed. Android requires explicit calls to createBond(), and the timing matters. Bond too early and you get pairing dialogs before the user expects them.

Plan for a BLE abstraction layer in your app architecture. Libraries like RxBLE (Android), CombineBluetooth (iOS), or the cross-platform flutter_reactive_ble package can help, but you will still hit platform-specific edge cases that require native debugging. Budget at least 20% of your BLE development time for platform-specific quirks.

### Connection Reliability Patterns

BLE connections drop. This is not a bug; it is the nature of wireless communication. Your app needs to handle reconnection gracefully. Implement an exponential backoff strategy for reconnection attempts: 1 second, 2 seconds, 4 seconds, up to a maximum of 30 seconds. Cache the last known device identifier so reconnection is faster than initial discovery. On iOS, use the retrievePeripherals(withIdentifiers:) method to reconnect to a known device without a full scan.

For data integrity, implement a sequence number system. Every data packet from the device includes a monotonically increasing sequence number. The app tracks the last received sequence number and requests retransmission of any gaps after reconnection. This is especially critical for medical devices or any application where data loss is unacceptable.

## Device Provisioning and Onboarding UX

The first 60 seconds of the user experience determine whether your connected product gets returned or becomes a daily habit. Provisioning, the process of pairing the device with the app and configuring it for the user, is where most companion apps fail. Industry data from Bluetooth SIG shows that 15% of consumers return smart home devices because they could not complete setup.

### The Provisioning Flow

A solid provisioning sequence follows this pattern: (1) the user opens the app and taps "Add Device," (2) the app scans for nearby BLE devices advertising your custom service UUID, (3) the user selects their device from the list (or the app auto-selects if only one is found), (4) the app connects and performs a handshake to verify the device is genuine, (5) the user names the device or assigns it to a room/location, and (6) the app completes any WiFi provisioning if the device also connects to the internet.

Keep the happy path under 30 seconds. Every extra tap or loading screen increases abandonment. Show progress indicators during scanning and connection. If something fails, give the user a specific recovery action ("Move closer to your device" or "Make sure the device is powered on") instead of a generic error code.

### WiFi Provisioning Over BLE

Many IoT devices use BLE only for initial setup and then switch to WiFi for ongoing cloud connectivity. The companion app collects WiFi credentials from the user and sends them to the device over a BLE characteristic. The device connects to WiFi and confirms success back through BLE. This is the standard flow for devices like smart speakers, cameras, and thermostats.

Security matters here. Never transmit WiFi credentials in plaintext over BLE. Use the device's public key (embedded in firmware) to encrypt the credentials before transmission. The ESP-IDF framework from Espressif includes a secure provisioning library (esp_prov) that handles this correctly. Nordic Semiconductor's nRF Connect SDK has similar functionality.

### Device Authentication

You need to verify that the device connecting to your app is actually your hardware, not a spoofed device. Implement a challenge-response authentication during provisioning. The app sends a random nonce to the device. The device signs it with a pre-provisioned private key and returns the signature. The app verifies the signature against the device's public key (fetched from your backend by serial number). This prevents counterfeit devices from connecting to your ecosystem.

For production, consider Microchip's ATECC608B ($0.65 per unit) or Infineon's OPTIGA Trust M ($1.20 per unit) secure elements. These chips store private keys in tamper-resistant hardware and perform cryptographic operations on-chip, so the key never leaves the device.

## Data Architecture: Local Storage, Sync, and Cloud Backend

A companion app manages three data stores: the device's onboard memory, the phone's local database, and your cloud backend. Getting data to flow correctly between all three, especially when connectivity is intermittent, is one of the core engineering challenges.

### On-Device Data Buffering

Your hardware device collects data continuously but can only transmit when the companion app is connected over BLE. A fitness band records heart rate every second, all day, but syncs only when the user opens the app. This means the device needs onboard flash storage to buffer hours or days of data. Common choices include a 4MB SPI flash chip ($0.50) or an SD card slot for devices that generate larger volumes like audio or images.

Design a simple file system or ring buffer on the device. When storage fills up, overwrite the oldest data. The companion app should sync from the last known timestamp and download everything newer. Transfer speeds over BLE at a comfortable MTU of 247 bytes and a connection interval of 30ms give you roughly 6 KB/s of throughput. A full day of sensor data (86,400 readings at 4 bytes each, roughly 337 KB) syncs in under a minute.

### Local Database on the Phone

Store synced data locally on the phone so the app works without an internet connection. SQLite (via Room on Android or Core Data on iOS) is the standard choice. For cross-platform apps, Drift (Flutter) or WatermelonDB (React Native) provide reactive database layers that update the UI automatically when new data arrives from the device.

Structure your local schema to mirror your cloud schema closely. Every record should have a device_id, timestamp, synced_to_cloud boolean, and the actual data payload. A background job uploads un-synced records to your cloud backend whenever the phone has internet connectivity. Mark records as synced only after receiving a 200 response from your API.

### Cloud Backend Design

Your cloud backend serves three purposes: long-term data storage, user account management, and analytics/insights generation. For most companion apps, a straightforward setup works well. Use PostgreSQL for relational data (users, devices, settings) and TimescaleDB or InfluxDB for time-series sensor data. A time-series database handles the insert-heavy, time-ordered workload far better than standard Postgres. InfluxDB Cloud starts free for up to 10,000 writes per minute.

Build your API with standard REST or GraphQL. The companion app calls your API to upload sensor data, fetch user settings, check for firmware updates, and retrieve aggregated analytics. For real-time features like live dashboards or remote device control, add WebSocket support or use a service like Ably or Pusher for managed pub/sub. As your [edge computing and IoT architecture](/blog/edge-computing-iot-app-development-guide) matures, you may push more processing to the device or a local gateway and send only aggregated results to the cloud.

![Cloud server infrastructure handling IoT device data synchronization and storage](https://images.unsplash.com/photo-1558494949-ef010cbdcc31?w=800&q=80)

## Firmware Over-the-Air (OTA) Updates

OTA firmware updates are non-negotiable for any connected device. You will discover bugs, need to patch security vulnerabilities, and want to ship new features after the hardware is in customers' hands. Getting OTA right is critical because a failed update can permanently brick a device, creating a support nightmare and potential recall.

### How BLE OTA Works

The basic flow: your backend stores the new firmware binary. The companion app checks for available updates on launch or on a schedule. When an update is available, the app downloads the binary to the phone, verifies its integrity (SHA-256 hash), and then transfers it to the device over BLE. The device writes the binary to a secondary flash partition, verifies it, and reboots into the new firmware.

Transfer speed is the bottleneck. A 256 KB firmware image takes roughly 45 seconds to transfer over BLE at 6 KB/s. A 1 MB image takes about 3 minutes. Show a progress bar with percentage and estimated time remaining. Do not let the user close the app or navigate away during transfer. On iOS, request a background task with beginBackgroundTask(withName:) to get a few extra minutes if the user switches apps.

### Dual-Bank (A/B) Partition Scheme

Never update firmware in place. Use a dual-bank scheme where the device has two firmware partitions: A (active) and B (staging). The new firmware writes to partition B while partition A continues running. After transfer completes, the device verifies partition B's integrity and reboots into it. If the new firmware fails to boot (watchdog timeout), the bootloader automatically rolls back to partition A. Nordic Semiconductor's nRF Connect SDK and Espressif's ESP-IDF both support this pattern out of the box.

### Update Safety Checklist

- **Battery check:** Refuse to start an OTA update if the device battery is below 30%. A dead battery mid-update means a bricked device.

- **Version validation:** The device should reject firmware versions older than or equal to the current version. This prevents rollback attacks.

- **Code signing:** Sign every firmware binary with your private key. The bootloader verifies the signature before booting. Use ECDSA P-256 for a good balance of security and performance on constrained hardware.

- **Staged rollout:** Push updates to 5% of devices first. Monitor crash rates and error reports for 48 hours. Then roll out to 25%, then 100%. Your backend needs a rollout percentage field per firmware version and device group.

- **Resume support:** If the BLE connection drops mid-transfer, the app should resume from the last acknowledged block, not restart from zero. Implement block-level acknowledgment in your transfer protocol.

Plan for OTA infrastructure from day one. Retrofitting it after launch is expensive and risky. The firmware side typically adds $10,000 to $20,000 to your development budget, but it pays for itself the first time you need to patch a critical bug across thousands of deployed devices.

## Testing Connected Device Apps: Hardware-in-the-Loop and Beyond

Testing a companion app is fundamentally different from testing a standard mobile app. You are testing software that depends on physical hardware, wireless communication, and environmental conditions. Flaky BLE connections in the office do not mean your code is broken. But sometimes they do. Figuring out which is which requires a deliberate testing strategy.

### Hardware-in-the-Loop (HIL) Testing

HIL testing connects your app to actual devices in an automated test environment. Set up a test bench with your device, a BLE sniffer (like the Nordic nRF Sniffer, $30), and a phone connected to your CI system via USB. Use tools like Appium or Detox to drive the app through provisioning, data sync, and OTA flows while the device is powered and in range. This catches real-world issues that mocked BLE connections miss: timing-dependent bugs, MTU negotiation failures, and connection parameter conflicts.

The downside: HIL testing is slow and requires physical hardware. Limit it to critical paths (provisioning, OTA, data sync) and run it nightly rather than on every commit. For a startup, 2 to 3 physical test devices per platform is sufficient. As you scale, companies like BLE test automation vendor Punchthrough offer managed HIL environments.

### BLE Simulation and Mocking

For unit and integration tests that run on every commit, mock the BLE layer. On Android, use MockBluetooth or shadow the BluetoothGatt class in Robolectric. On iOS, create a protocol that wraps CBCentralManager and inject a mock implementation in tests. For Flutter, the flutter_reactive_ble package supports a mock mode out of the box.

A more sophisticated approach is to run a BLE simulator on a second phone or Raspberry Pi that acts as your device. Nordic's nRF Connect app can emulate a GATT server with custom services and characteristics. This lets you test the app against a "device" without flashing firmware. It is especially useful for testing error conditions: you can simulate a device that disconnects mid-transfer, returns corrupted data, or has an outdated firmware version.

### Field Testing Matters

BLE range and reliability vary dramatically by environment. A connection that works perfectly at your desk may fail in a kitchen full of 2.4 GHz interference (WiFi routers, microwaves, baby monitors). Test in the environments where your product will actually be used. For [wearable and health device integrations](/blog/wearable-ai-health-device-integration-guide), this means testing while the user is moving, exercising, or in a crowded gym with dozens of other BLE devices nearby.

Collect BLE connection metrics from your production app: connection success rate, average connection time, disconnection frequency, and data transfer throughput. Instrument these from day one. They are the early warning system for hardware revisions, firmware bugs, and environmental issues that your test bench will never catch.

![Developer writing code for IoT companion app firmware and BLE integration testing](https://images.unsplash.com/photo-1555949963-ff9fe0c870eb?w=800&q=80)

## Costs, Timelines, and Getting Started

Let's talk real numbers. Building a connected device companion app is a significant investment, but understanding the cost breakdown helps you budget accurately and avoid surprises.

### Development Cost Breakdown

- **BLE communication layer:** $25,000 to $50,000. This covers GATT profile design, platform-specific BLE code, connection management, and data transfer protocols. iOS and Android have different enough BLE stacks that you are essentially building this twice (or using a cross-platform bridge with native fallbacks).

- **Device provisioning and onboarding:** $15,000 to $30,000. Scanning, pairing, WiFi provisioning (if applicable), device authentication, and the user-facing setup flow.

- **Data sync and local storage:** $20,000 to $40,000. Local database, background sync engine, conflict resolution, and offline support.

- **Cloud backend:** $30,000 to $60,000. User management, device registry, time-series data ingestion, API layer, and basic analytics dashboard.

- **Firmware OTA system:** $10,000 to $20,000 (app side). The firmware engineering is separate and depends on your hardware platform.

- **UI/UX design:** $15,000 to $30,000. Dashboard screens, device status views, data visualizations, settings, and onboarding flows.

- **Testing and QA:** $15,000 to $25,000. HIL test setup, BLE simulation, field testing, and cross-device compatibility.

Total MVP range: $130,000 to $255,000 for a single platform. Add 60 to 70% for both iOS and Android with native development, or 30 to 40% more for cross-platform (React Native or Flutter with native BLE modules).

### Timeline Estimates

With a team of 2 mobile engineers, 1 backend engineer, and 1 firmware engineer working in parallel:

- **Weeks 1 to 4:** Architecture design, BLE protocol definition, GATT profile specification, and backend API design.

- **Weeks 5 to 10:** Core BLE communication, provisioning flow, basic data sync, and backend scaffolding.

- **Weeks 11 to 16:** OTA implementation, advanced data features, analytics dashboard, and notification system.

- **Weeks 17 to 20:** Integration testing, field testing, performance optimization, and beta program.

- **Weeks 21 to 24:** Bug fixes from beta feedback, App Store submission, and production deployment.

Total: approximately 6 months from kickoff to app store launch. This assumes your firmware is being developed in parallel and is stable enough for integration by week 8.

### Technology Stack Recommendations

For most startups building their first companion app, here is what we recommend: Flutter or React Native for the mobile app (with native BLE bridges), Node.js or Python (FastAPI) for the backend API, PostgreSQL plus TimescaleDB for data storage, AWS IoT Core or a lightweight MQTT broker for device communication, and GitHub Actions for CI/CD with HIL testing on a nightly schedule. If your device also connects to WiFi, consider the ESP32 platform from Espressif. It has excellent BLE and WiFi support, solid documentation, and a massive community.

Building a [smart home IoT app](/blog/how-to-build-a-smart-home-iot-app) shares many of these architectural patterns, especially around device discovery, local control, and cloud sync. The difference with companion apps is the depth of integration with a single device type versus broad compatibility across many.

If you are planning a connected device and need a companion app that actually works reliably across platforms, we have shipped these for hardware startups ranging from wearable health monitors to industrial sensors. The BLE layer alone has enough gotchas to derail an inexperienced team by months. [Book a free strategy call](/get-started) and we will walk through your device architecture, identify the highest-risk technical areas, and scope a realistic development plan.

---

*Originally published on [Kanopy Labs](https://kanopylabs.com/blog/how-to-build-a-connected-device-companion-app)*
