将数据写入蓝牙外设失败

时间:2016-08-14 18:08:25

标签: ios swift bluetooth core-bluetooth hm-10

我有一个在Windows PC上运行的程序,它通过COM端口发送/接收数据。数据通过HM10蓝牙模块通过蓝牙传输。

我能够发现外围设备,连接它,成功地发现它的服务和特性。不过我的问题是发送数据。中央是我的iPhone。 PC充当外围设备。

首先是我的代码。

import CoreBluetooth
import UIKit

class ViewController: UIViewController {

    @IBOutlet weak var peripheralNameLabel: UILabel!
    @IBOutlet weak var noOfServicesLabel: UILabel!
    @IBOutlet weak var sendBytesButton: UIButton!
    @IBOutlet weak var sendStringButton: UIButton!
    @IBOutlet weak var responseTextView: UITextView!

    private let serviceUUID = CBUUID(string: "FFE0")
    private var characteristicUUID = CBUUID(string: "FFE1")

    private var manager: CBCentralManager!
    private var peripheral: CBPeripheral!
    private var characteristic: CBCharacteristic!

    override func viewDidLoad() {
        super.viewDidLoad()

        manager = CBCentralManager(delegate: self, queue: nil)
    }

    @IBAction func didTapSendBytesButton(sender: UIButton) {
        let bytes: [UInt8] = [0x35, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
        let data = NSData(bytes: bytes, length: bytes.count)
        peripheral.writeValue(data, forCharacteristic: characteristic, type: .WithResponse)
    }

    @IBAction func didTapSendStringButton(sender: UIButton) {
        let string = "5100000000"
        if let data = string.dataUsingEncoding(NSUTF8StringEncoding) {
            peripheral.writeValue(data, forCharacteristic: characteristic, type: .WithResponse)
        } else {
            sendStringButton.enabled = false
            sendStringButton.setTitle("", forState: .Normal)
        }
    }

}

extension ViewController: CBCentralManagerDelegate {

    func centralManagerDidUpdateState(central: CBCentralManager) {
        print(#function)

        switch central.state {
        case .Unsupported:
            print("Unsupported")
        case .Unauthorized:
            print("Unauthorized")
        case .PoweredOn:
            print("Powered On")
            navigationItem.title = "Connecting..."
            central.scanForPeripheralsWithServices([serviceUUID], options: nil)
        case .Resetting:
            print("Resetting")
        case .PoweredOff:
            print("Powered Off")
        case .Unknown:
            print("Unknown")
        }
    }

    func centralManager(central: CBCentralManager, didDiscoverPeripheral peripheral: CBPeripheral, advertisementData: [String : AnyObject], RSSI: NSNumber) {
        print(#function)

        print("Discovered \(peripheral.name) at \(RSSI)")
        peripheralNameLabel.text = peripheral.name

        if peripheral.name == nil || peripheral.name == "" {
            return
        }

        if self.peripheral == nil || self.peripheral.state == .Disconnected {
            self.peripheral = peripheral
            central.connectPeripheral(peripheral, options: nil)

            central.stopScan()
        }
    }

    func centralManager(central: CBCentralManager, didConnectPeripheral peripheral: CBPeripheral) {
        print(#function)
        navigationItem.title = "Connected!"
        sendBytesButton.enabled = true
        sendStringButton.enabled = true

        peripheral.delegate = self
        peripheral.discoverServices([serviceUUID])
    }

    func centralManager(central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: NSError?) {
        self.peripheral = nil
        central.scanForPeripheralsWithServices(nil, options: nil)
    }

    func centralManager(central: CBCentralManager, didFailToConnectPeripheral peripheral: CBPeripheral, error: NSError?) {
        print(#function)
        self.peripheral = nil
    }

}

extension ViewController: CBPeripheralDelegate {

    func peripheral(peripheral: CBPeripheral, didDiscoverServices error: NSError?) {
        print(#function)

        guard let services = peripheral.services else {
            return
        }

        noOfServicesLabel.text = "\(services.count)"

        for service in services {
            print(service.UUID)
            if service.UUID == serviceUUID {
                peripheral.discoverCharacteristics(nil, forService: service)
            }
        }
    }

    func peripheral(peripheral: CBPeripheral, didDiscoverCharacteristicsForService service: CBService, error: NSError?) {
        print(#function)

        guard let characteristics = service.characteristics else {
            return
        }

        for characteristic in characteristics {
            print("characteristic: \(characteristic.UUID)")
            if characteristic.UUID == characteristicUUID {
                self.characteristic = characteristic
                peripheral.setNotifyValue(true, forCharacteristic: characteristic)
            }
        }
    }

    func peripheral(peripheral: CBPeripheral, didWriteValueForCharacteristic characteristic: CBCharacteristic, error: NSError?) {
        print(#function)
        print(error)
    }

    func peripheral(peripheral: CBPeripheral, didUpdateValueForCharacteristic characteristic: CBCharacteristic, error: NSError?) {
        print(#function)

        if characteristic.UUID == characteristicUUID {
            print("Got reply from: \(characteristic.UUID)")
            if let data = characteristic.value, let string = String(data: data, encoding: NSUTF8StringEncoding) {
                responseTextView.text = string
            } else {
                print("No response!")
            }
        }
    }

}

我应该发送像这样的字节数组,

0x35 0x31 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00

到外围设备,如果外围设备成功接收到它,我会将此作为响应,

0x43 0x41 0x4E 0x20 0x31 0x31 0x2F 0x35 0x30 0x30 0x0D 0x0A

它应该是什么样子。

enter image description here

这才是真正发生的事情。

enter image description here

enter image description here

enter image description here

数据包在传输时被分成几部分。因此,我没有得到成功的回应。

奇怪的部分有时很少,它实际上是有效的!数据正确发送(如第一张图片中所示),我收到成功响应。但失败经常发生。像99%的时间一样。

我尝试了两种方式,将数据作为字节数组(didTapSendBytesButton())发送,并将其作为转换后的字符串(didTapSendStringButton())发送。两者都是相同的。

还使用名为Bluetooth Serial的应用进行了测试。结果相同。

我无法弄清楚为什么会这样。

1 个答案:

答案 0 :(得分:1)

您发送的字节少于20个字节,因此蓝牙数据将通过一次传输发送。

问题在于您的接收方,或实际上您的通信方式。

一个串口一次只能发送或接收一个字节,所以即使iOS一次发送所有字节(这是一个副作用,如何通过GATT模拟串口),Windows必须呈现它们一次一个到虚拟COM端口驱动程序。 Windows在COM端口上进行缓冲,以便在程序读取速度不够快时不会丢失字节,这就是为什么每个“RX”看到多个字节的原因,但COM驱动程序中没有“数据包”的概念,因此它不知道发送了多少字节,或者它应该等到收到所有字节并将它们作为一个组传送。

最终的答案是你需要修改你的消息,以便它有某种分隔符(甚至是简单的分隔符),让接收程序知道已收到消息的结尾。然后,它可以验证消息并进行适当的响应。

或者,如果您无法控制接收程序,并且该程序与其他发送代码一起工作,那么我怀疑您发送的数据有问题,因为串行字节被拆分的事实多个RX调用是正常的,必须编写接收程序来处理这个