2016-08-14 263 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