Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Core Bluetooth: Send data between 2 iOS devices

I've been programming with swift for a bit now, but I'm completely new to Core Bluetooth. Is there a way to send primitive data (integers, characters) from one iOS device to another using Core Bluetooth?

Thanks in advance

like image 631
M Z Avatar asked Sep 15 '25 08:09

M Z


2 Answers

Yes. You need to make one device peripheral and the second central(or both of them). In the peripheral you need to advertise data(peripheralManager.startAdvertising) and in the central you need to get it by the characteristic. You can read all about that in https://developer.apple.com/documentation/corebluetooth

like image 91
ironRoei Avatar answered Sep 18 '25 08:09

ironRoei


Here is the code related to CBCentral and CBPeripheral in Swift.

CBCentral

After Scan & Connect

      /** We've connected to the peripheral, now we need to discover the services and characteristics to find the 'transfer' characteristic.
         */
        func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
            print("Peripheral Connected")

            // Stop scanning
            centralManager?.stopScan()
            print("Scanning stopped")

            // Clear the data that we may already have
            data.length = 0

            // Make sure we get the discovery callbacks
            peripheral.delegate = self

            // Search only for services that match our UUID
            peripheral.discoverServices([transferServiceUUID])
        }

        /** The Transfer Service was discovered
         */
        func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
            guard error == nil else {
                print("Error discovering services: \(error!.localizedDescription)")
                cleanup()
                return
            }

            guard let services = peripheral.services else {
                return
            }

            // Discover the characteristic we want...

            // Loop through the newly filled peripheral.services array, just in case there's more than one.
            for service in services {
                peripheral.discoverCharacteristics([transferCharacteristicUUID], for: service)
            }
        }

        /** The Transfer characteristic was discovered.
         *  Once this has been found, we want to subscribe to it, which lets the peripheral know we want the data it contains
         */
        func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
            // Deal with errors (if any)
            guard error == nil else {
                print("Error discovering services: \(error!.localizedDescription)")
                cleanup()
                return
            }


            guard let characteristics = service.characteristics else {
                return
            }

            // Again, we loop through the array, just in case.
            for characteristic in characteristics {
                // And check if it's the right one
                if characteristic.uuid.isEqual(transferCharacteristicUUID) {
                    // If it is, subscribe to it
                    peripheral.setNotifyValue(true, for: characteristic)
                }
            }
            // Once this is complete, we just need to wait for the data to come in.
        }

        /** This callback lets us know more data has arrived via notification on the characteristic
         */
        func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
            guard error == nil else {
                print("Error discovering services: \(error!.localizedDescription)")
                return
            }

            guard let stringFromData = NSString(data: characteristic.value!, encoding: String.Encoding.utf8.rawValue) else {
                print("Invalid data")
                return
            }

            // Have we got everything we need?
            if stringFromData.isEqual(to: "EOM") {
                // We have, so show the data,
                textView.text = String(data: data.copy() as! Data, encoding: String.Encoding.utf8)

                // Cancel our subscription to the characteristic
                peripheral.setNotifyValue(false, for: characteristic)

                // and disconnect from the peripehral
                centralManager?.cancelPeripheralConnection(peripheral)
            } else {
                // Otherwise, just add the data on to what we already have
                data.append(characteristic.value!)

                // Log it
                print("Received: \(stringFromData)")
            }
        }

CBPeripheral

After Scan & Connect

 /** Catch when someone subscribes to our characteristic, then start sending them data
  */
 func peripheralManager(_ peripheral: CBPeripheralManager, central: CBCentral, didSubscribeTo characteristic: CBCharacteristic) {
    print("Central subscribed to characteristic")

    // Get the data
    dataToSend = textView.text.data(using: String.Encoding.utf8)

    // Reset the index
    sendDataIndex = 0;

    // Start sending
    sendData()
 }

 /** Recognise when the central unsubscribes
  */
 func peripheralManager(_ peripheral: CBPeripheralManager, central: CBCentral, didUnsubscribeFrom characteristic: CBCharacteristic) {
    print("Central unsubscribed from characteristic")
 }

 // First up, check if we're meant to be sending an EOM
 fileprivate var sendingEOM = false;

 /** Sends the next amount of data to the connected central
  */
 fileprivate func sendData() {
    if sendingEOM {
        // send it
        let didSend = peripheralManager?.updateValue(
            "EOM".data(using: String.Encoding.utf8)!,
            for: transferCharacteristic!,
            onSubscribedCentrals: nil
        )

        // Did it send?
        if (didSend == true) {

            // It did, so mark it as sent
            sendingEOM = false

            print("Sent: EOM")
        }

        // It didn't send, so we'll exit and wait for peripheralManagerIsReadyToUpdateSubscribers to call sendData again
        return
    }

    // We're not sending an EOM, so we're sending data

    // Is there any left to send?
    guard sendDataIndex < dataToSend?.count else {
        // No data left.  Do nothing
        return
    }

    // There's data left, so send until the callback fails, or we're done.
    // Can't be longer than 20 bytes

    var didSend = true

    while didSend {
        // Make the next chunk

        // Work out how big it should be
        var amountToSend = dataToSend!.count - sendDataIndex!;


        // Copy out the data we want
        let chunk = dataToSend!.withUnsafeBytes{(body: UnsafePointer<UInt8>) in
            return Data(
                bytes: body + sendDataIndex!,
                count: amountToSend
            )
        }

        // Send it
        didSend = peripheralManager!.updateValue(
            chunk as Data,
            for: transferCharacteristic!,
            onSubscribedCentrals: nil
        )

        // If it didn't work, drop out and wait for the callback
        if (!didSend) {
            return
        }

        let stringFromData = NSString(
            data: chunk as Data,
            encoding: String.Encoding.utf8.rawValue
        )

        print("Sent: \(stringFromData)")

        // It did send, so update our index
        sendDataIndex! += amountToSend;

        // Was it the last one?
        if (sendDataIndex! >= dataToSend!.count) {

            // It was - send an EOM

            // Set this so if the send fails, we'll send it next time
            sendingEOM = true

            // Send it
            let eomSent = peripheralManager!.updateValue(
                "EOM".data(using: String.Encoding.utf8)!,
                for: transferCharacteristic!,
                onSubscribedCentrals: nil
            )

            if (eomSent) {
                // It sent, we're all done
                sendingEOM = false
                print("Sent: EOM")
            }

            return
        }
    }
 }

 /** This callback comes in when the PeripheralManager is ready to send the next chunk of data.
  *  This is to ensure that packets will arrive in the order they are sent
  */
 func peripheralManagerIsReady(toUpdateSubscribers peripheral: CBPeripheralManager) {
    // Start sending again
    sendData()
 }
like image 29
Rizwan Mehboob Avatar answered Sep 18 '25 08:09

Rizwan Mehboob