0

我有一个在 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.

它应该是什么样子。

在此处输入图像描述

这是真正发生的事情。

在此处输入图像描述

在此处输入图像描述

在此处输入图像描述

数据包在传输时被分解成碎片。因此我没有得到成功的响应。

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

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

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

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

4

1 回答 1

1

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

问题在于您的接收方,或者实际上是您如何构建通信。

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

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

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

于 2016-08-14T20:49:22.717 回答