2016-08-14 121 views
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

它应该是什么样子。

enter image description here

这到底发生了什么。

enter image description here

enter image description here

enter image description here

数据分组传输时被破碎成碎片。所以我没有得到成功的回应。

奇怪的部分有时很少它实际上起作用!数据得到正确发送(如第一张图所示),我收到成功响应。但失败的发生往往不是。像99%的时间。

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

还使用名为Bluetooth Serial的应用测试了它。同样的结果。

我不明白为什么会发生这种情况。

+0

您可以在这里引用iOS和Windows--您使用哪种发送数据?你想和哪个外设通话? – Carter

+0

对不起,如果我不清楚。我会更新答案。 iOS设备充当中心。 PC作为外设。 – Isuru

+0

您使用蓝牙串口应用程序进行了测试,并且具有相同的行为,是不是表示问题出现在接收端?您接收数据的代码是什么? – Carter

回答

1

您发送的字节少于20个字节,因此蓝牙数据将以单个传输方式发送。

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

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

最终的答案是,您需要修改您的消息,使其具有某种分隔符(即使是简单的\ n),以便接收程序知道已收到消息的结尾。然后它可以验证消息并做出适当的响应。或者,如果你不能控制接收程序,并且该程序与其他发送代码一起工作,那么我怀疑你的发送数据有问题,因为串行字节分裂为多个字符RX调用是正常的,并且必须写入接收程序来处理这个问题

+0

感谢您的详细解答,保罗。我被告知,在Windows上运行的程序查找10个字节大约100ms,然后超时。这对此有什么影响? – Isuru

+0

可能,但问题将出在PC上的虚拟COM驱动程序,而不是iOS代码或BLE设备。 100ms应该有足够的时间以9600bps传输10个字节 – Paulw11