Implementing Data Exchange with BLE Peripheral

NOVASOLUTIONS.TECHNOLOGY is engaged in the development, support and maintenance of iOS, Android, PWA mobile applications. We have extensive experience and expertise in publishing mobile applications in popular markets like Google Play, App Store, Amazon, AppGallery and others.
Development and support of all types of mobile applications:
Information and entertainment mobile applications
News apps, games, reference guides, online catalogs, weather apps, fitness and health apps, travel apps, educational apps, social networks and messengers, quizzes, blogs and podcasts, forums, aggregators
E-commerce mobile applications
Online stores, B2B apps, marketplaces, online exchanges, cashback services, exchanges, dropshipping platforms, loyalty programs, food and goods delivery, payment systems.
Business process management mobile applications
CRM systems, ERP systems, project management, sales team tools, financial management, production management, logistics and delivery management, HR management, data monitoring systems
Electronic services mobile applications
Classified ads platforms, online schools, online cinemas, electronic service platforms, cashback platforms, video hosting, thematic portals, online booking and scheduling platforms, online trading platforms

These are just some of the types of mobile applications we work with, and each of them may have its own specific features and functionality, tailored to the specific needs and goals of the client.

Showing 1 of 1 servicesAll 1735 services
Implementing Data Exchange with BLE Peripheral
Complex
~3-5 business days
FAQ
Our competencies:
Development stages
Latest works
  • image_mobile-applications_feedme_467_0.webp
    Development of a mobile application for FEEDME
    756
  • image_mobile-applications_xoomer_471_0.webp
    Development of a mobile application for XOOMER
    624
  • image_mobile-applications_rhl_428_0.webp
    Development of a mobile application for RHL
    1050
  • image_mobile-applications_zippy_411_0.webp
    Development of a mobile application for ZIPPY
    947
  • image_mobile-applications_affhome_429_0.webp
    Development of a mobile application for Affhome
    862
  • image_mobile-applications_flavors_409_0.webp
    Development of a mobile application for the FLAVORS company
    445

BLE Peripheral Data Exchange Implementation

After connection is established and services discovered, main work begins: reading values, writing commands, subscribing to notifications. This part of BLE stack requires understanding GATT attributes, MTU and platform-specific details when working with binary data.

Characteristic Operation Types

A characteristic has properties flags that define available operations:

Flag iOS (CBCharacteristicProperties) Android Operation
Read .read PROPERTY_READ One-time read
Write .write PROPERTY_WRITE Write with confirmation
Write Without Response .writeWithoutResponse PROPERTY_WRITE_NO_RESPONSE Fast write
Notify .notify PROPERTY_NOTIFY Notifications without confirmation
Indicate .indicate PROPERTY_INDICATE Notifications with confirmation

Write Without Response faster — no ACK from device. Suitable for streaming (audio, sensor readings). Write — for commands where delivery guarantee matters.

iOS: Working with Data

Notify Subscription and Parsing

// Enable notify
peripheral.setNotifyValue(true, for: characteristic)

// Receive data
func peripheral(_ peripheral: CBPeripheral,
                didUpdateValueFor characteristic: CBCharacteristic,
                error: Error?) {
    guard error == nil, let data = characteristic.value else { return }

    // Example: sensor sends 3 bytes [flags, heartRate, energyExpended]
    guard data.count >= 2 else { return }
    let flags = data[0]
    let heartRate: Int

    if flags & 0x01 == 0 {
        // heart rate in 1 byte
        heartRate = Int(data[1])
    } else {
        // heart rate in 2 bytes (little-endian)
        heartRate = Int(data[1]) | (Int(data[2]) << 8)
    }
}

Working with binary data via Data + byte offsets. For nonstandard devices with sparse documentation — Wireshark + BLE sniffer (Ellisys, Nordic nRF Sniffer) helps parse protocol.

Write Command

func sendCommand(_ command: UInt8, value: UInt16) {
    var bytes: [UInt8] = [command, UInt8(value & 0xFF), UInt8(value >> 8)]
    let data = Data(bytes)

    let writeType: CBCharacteristicWriteType = characteristic.properties.contains(.writeWithoutResponse)
        ? .withoutResponse
        : .withResponse

    peripheral.writeValue(data, for: characteristic, type: writeType)
}

With .withResponse — wait for didWriteValueFor callback. With .withoutResponse on iOS 11+ check peripheral.canSendWriteWithoutResponse before sending, otherwise data lost on buffer overflow.

Android: BluetoothGatt in Detail

Notify + CCCD Descriptor

Notify subscription requires two steps: enable notify locally and write to CCCD (Client Characteristic Configuration Descriptor) on device:

fun enableNotification(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic) {
    // Step 1: enable locally
    gatt.setCharacteristicNotification(characteristic, true)

    // Step 2: write descriptor on device
    val cccd = characteristic.getDescriptor(
        UUID.fromString("00002902-0000-1000-8000-00805f9b34fb")
    ) ?: return

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
        gatt.writeDescriptor(cccd, BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE)
    } else {
        @Suppress("DEPRECATION")
        cccd.value = BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE
        @Suppress("DEPRECATION")
        gatt.writeDescriptor(cccd)
    }
}

Step 2 often skipped — and notifications don't arrive. This is most common notify error.

Operation Sequence

One critical Android BLE detail: cannot execute multiple GATT operations simultaneously. Only one operation in flight. Send next after callback from previous.

Violating this rule causes error status 133 or data loss on most Android devices.

Solution — queue:

class BleOperationQueue {
    private val queue: LinkedList<BleOperation> = LinkedList()
    private var operationInProgress = false

    fun enqueue(operation: BleOperation) {
        queue.add(operation)
        if (!operationInProgress) {
            executeNext()
        }
    }

    fun onOperationCompleted() {
        operationInProgress = false
        executeNext()
    }

    private fun executeNext() {
        val op = queue.poll() ?: return
        operationInProgress = true
        op.execute()
    }
}

MTU and Large Data

Default MTU = 23 bytes (20 bytes payload after GATT header). For firmware transmission or large configs this catastrophically small.

Request increased MTU immediately after connection:

// iOS
peripheral.maximumWriteValueLength(for: .withoutResponse) // returns current max
// MTU negotiation automatic via iOS 9+, can influence indirectly
// Android
gatt.requestMtu(512) // in onMtuChanged we get real negotiated size

In practice most BLE chips support MTU 247–512 bytes. This turns 10KB transmission from 500 packets to 20–40.

Implementation timeframe: 3–5 days — full data exchange with operation queue, MTU negotiation and error handling on both platforms. Cost calculated individually.