追記あり;Bluetoothでデータ送受信
★
func peripheral(peripheral: CBPeripheral!, didWriteValueForCharacteristic characteristic: CBCharacteristic!, error: NSError!) ...と
@IBAction func writeBtnTapped(sender: UIButton)...の項目を追加して、セントラル側からペリフェラル側へ
データを送れるようにしました。
これでペリフェラルとセントラルの双方でデータのやりとりができます。
Bluetoothでデータ送受信というと、画像などのメディアも送受信できるMultipeer Connectivityを使うのがスムーズなのかもですが、
どうやらCore Bluetoothのほうが電池をくわないそうなので、大変でしたがこっちで書いてみました。
こちらの本「iOS×BLE Core Bluetoothプログラミング」をベースにSwiftで書いています。
この本はとても素晴らしい本なのですが、Swiftでない部分もあるので、そこは補完しました。
まず最初にプロジェクトの設定画面でCoreBluetoothのFrameworkを読み込んでおきます。
Linked Frameworks and Librariesの+をクリックして、CoreBluetooth.Frameworkを選ぶだけ。
以下はペリフェラル(データ送る)側でボタンを押すとその値がセントラル(データ受信側)に送られて、
セントラル側でその値に応じたテキストとUIImageが表示されます。
ペリフェラル側でトリガーを送ってあげて、セントラル側でそのトリガーに合ったアクションが起きるという感じですね。
・ペリフェラル(データ送る)側
import UIKit import CoreBluetooth class ViewController: UIViewController, CBPeripheralManagerDelegate { @IBOutlet var advertiseBtn: UIButton! @IBOutlet var valueLabel: UILabel! @IBOutlet var strLabel: UILabel! var peripheralManager: CBPeripheralManager! var serviceUUID: CBUUID! var characteristic: CBMutableCharacteristic! var data:NSData! override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view, typically from a nib. self.peripheralManager = CBPeripheralManager(delegate: self, queue: nil, options: nil) self.valueLabel.text = nil; } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() // Dispose of any resources that can be recreated. } func publishservice () { // サービスを作成 self.serviceUUID = CBUUID(string: "0000") let service = CBMutableService(type: serviceUUID, primary: true) // キャラクタリスティックを作成 let characteristicUUID = CBUUID(string: "0001") let properties = ( CBCharacteristicProperties.Notify | CBCharacteristicProperties.Read | CBCharacteristicProperties.Write) let permissions = ( CBAttributePermissions.Readable | CBAttributePermissions.Writeable) self.characteristic = CBMutableCharacteristic( type: characteristicUUID, properties: properties, value: nil, permissions: permissions) // キャラクタリスティックをサービスにセット service.characteristics = [self.characteristic] // サービスを Peripheral Manager にセット self.peripheralManager.addService(service) var msg: String = "0" let data: NSData! = msg.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion:true) println("data: \(data)") self.characteristic.value = data; } func startAdvertise() { // アドバタイズメントデータを作成する let advertisementData: Dictionary = [ CBAdvertisementDataLocalNameKey: "Test Device", CBAdvertisementDataServiceUUIDsKey: [self.serviceUUID] ] // アドバタイズ開始 self.peripheralManager.startAdvertising(advertisementData) self.advertiseBtn.setTitle("STOP ADVERTISING", forState: UIControlState.Normal) } func stopAdvertise () { // アドバタイズ停止 self.peripheralManager.stopAdvertising() self.advertiseBtn.setTitle("START ADVERTISING", forState: UIControlState.Normal) } //データ送信 func updateValueLabel () { let reportData = UnsafePointer<UInt8>(data.bytes) var bpm : UInt16 bpm = UInt16(reportData[0]) bpm = CFSwapInt16LittleToHost(bpm) let outputString = String(bpm) self.valueLabel.text = "分類用文字: \(outputString)" switch outputString { case "1": self.strLabel.text = "いちです" case "2": self.strLabel.text = "にです" case "3": self.strLabel.text = "さんです" default: self.strLabel.text = "その他" //ランダムな数値を出力する場合 //self.valueLabel.text = NSString(format: "Characteristic value: %@", self.characteristic.value) as String } } // ペリフェラルマネージャの状態が変化すると呼ばれる func peripheralManagerDidUpdateState(peripheral: CBPeripheralManager!) { println("state: \(peripheral.state)") switch peripheral.state { case CBPeripheralManagerState.PoweredOn: // サービス登録開始 self.publishservice() break default: break } } // サービス追加処理が完了すると呼ばれる func peripheralManager(peripheral: CBPeripheralManager!, didAddService service: CBService!, error: NSError!) { if (error != nil) { println("サービス追加失敗! error: \(error)") return } println("サービス追加成功!") // アドバタイズ開始 self.startAdvertise() } // アドバタイズ開始処理が完了すると呼ばれる func peripheralManagerDidStartAdvertising(peripheral: CBPeripheralManager!, error: NSError!) { if (error != nil) { println("アドバタイズ開始失敗! error: \(error)") return } println("アドバタイズ開始成功!") } // Readリクエスト受信時に呼ばれる func peripheralManager(peripheral: CBPeripheralManager!, didReceiveReadRequest request: CBATTRequest!) { println("Readリクエスト受信! requested service uuid:\(request.characteristic.service.UUID) characteristic uuid:\(request.characteristic.UUID) value:\(request.characteristic.value)") // プロパティで保持しているキャラクタリスティックへのReadリクエストかどうかを判定 if request.characteristic.UUID.isEqual(self.characteristic.UUID) { // CBMutableCharacteristicのvalueをCBATTRequestのvalueにセット request.value = self.characteristic.value; // リクエストに応答 self.peripheralManager.respondToRequest(request, withResult: CBATTError.Success) } } // Writeリクエスト受信時に呼ばれる func peripheralManager(peripheral: CBPeripheralManager!, didReceiveWriteRequests requests: [AnyObject]!) { println("\(requests.count) 件のWriteリクエストを受信!") for obj in requests { if let request = obj as? CBATTRequest { println("Requested value:\(request.value) service uuid:\(request.characteristic.service.UUID) characteristic uuid:\(request.characteristic.UUID)") if request.characteristic.UUID.isEqual(self.characteristic.UUID) { // CBCharacteristicのvalueに、CBATTRequestのvalueをセット self.characteristic.value = request.value; } } } // リクエストに応答 self.peripheralManager.respondToRequest(requests[0] as! CBATTRequest, withResult: CBATTError.Success) } // Notify開始リクエスト受信時に呼ばれる func peripheralManager(peripheral: CBPeripheralManager!, central: CBCentral!, didSubscribeToCharacteristic characteristic: CBCharacteristic!) { println("Notify開始リクエストを受信") println("Notify中のセントラル: \(self.characteristic.subscribedCentrals)") } // Notify停止リクエスト受信時に呼ばれる func peripheralManager(peripheral: CBPeripheralManager!, central: CBCentral!, didUnsubscribeFromCharacteristic characteristic: CBCharacteristic!) { println("Notify停止リクエストを受信") println("Notify中のセントラル: \(self.characteristic.subscribedCentrals)") } @IBAction func advertiseBtnTap(sender: UIButton) { if (!self.peripheralManager.isAdvertising) { self.startAdvertise() } else { self.stopAdvertise() } } @IBAction func updateBtn1(sender: UIButton) { // 新しい値となるNSDataオブジェクトを生成 //1の値を送ったとき let valuemoto = 1 let value = UInt8(valuemoto & 0xFF) data = NSData(bytes: [value] as [UInt8], length: 1) // 値を更新 self.characteristic.value = data; let result = self.peripheralManager.updateValue( data, forCharacteristic: self.characteristic, onSubscribedCentrals: nil) println("resultだよ: \(result)") self.updateValueLabel() } @IBAction func updateBtn2(sender: UIButton) { // 新しい値となるNSDataオブジェクトを生成 //2の値を送ったとき let valuemoto = 2 let value = UInt8(valuemoto & 0xFF) data = NSData(bytes: [value] as [UInt8], length: 1) // 値を更新 self.characteristic.value = data; let result = self.peripheralManager.updateValue( data, forCharacteristic: self.characteristic, onSubscribedCentrals: nil) println("resultだよ: \(result)") self.updateValueLabel() } @IBAction func updateBtn3(sender: UIButton) { // 新しい値となるNSDataオブジェクトを生成 //3の値を送ったとき let valuemoto = 3 let value = UInt8(valuemoto & 0xFF) data = NSData(bytes: [value] as [UInt8], length: 1) // 値を更新 self.characteristic.value = data; let result = self.peripheralManager.updateValue( data, forCharacteristic: self.characteristic, onSubscribedCentrals: nil) println("resultだよ: \(result)") self.updateValueLabel() } @IBAction func updateBtnRan(sender: UIButton) { //ランダムな値を送ったとき let value = UInt8(arc4random() & 0xFF) let data = NSData(bytes: [value] as [UInt8], length: 1) self.characteristic.value = data; let result = self.peripheralManager.updateValue( data, forCharacteristic: self.characteristic, onSubscribedCentrals: nil) println("resultだよ: \(result)") self.updateValueLabel() } }
・セントラル(データを受信してそのデータに応じたアクションが起きるようにした)側
//データ受信側 import UIKit import CoreBluetooth //プロパティ定義 class ViewController: UIViewController, CBCentralManagerDelegate,CBPeripheralDelegate { @IBOutlet weak var valueLabel: UILabel! @IBOutlet weak var pictNumLabel: UILabel! @IBOutlet weak var myimageView: UIImageView! var isScanning = false var centralManager: CBCentralManager! var peripheral: CBPeripheral! var characteristic:CBCharacteristic! var aCharacteristic: CBCharacteristic! var outputString:String! override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view, typically from a nib. //セントラルマネージャ初期化 self.centralManager = CBCentralManager(delegate: self, queue: nil) self.valueLabel.text = nil; } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() // Dispose of any resources that can be recreated. } //データを表示 func updateValue() { var data2 = self.characteristic.value let reportData = UnsafePointer<UInt8>(data2.bytes) var bpm : UInt16 bpm = UInt16(reportData[0]) bpm = CFSwapInt16LittleToHost(bpm) let outputString = String(bpm) self.valueLabel.text = "文字変換: \(outputString)" switch outputString { case "1": pictNumLabel.text = "いちです" myimageView.image = UIImage(named: "testPic1.png") case "2": pictNumLabel.text = "にです" myimageView.image = UIImage(named: "testPic2.png") case "3": pictNumLabel.text = "さんです" myimageView.image = UIImage(named: "testPic3.png") case "4": pictNumLabel.text = "ランダム" myimageView.image = UIImage(named: "testPic4.png") default: pictNumLabel.text = "その他" myimageView.image = UIImage(named: "testPic1.png") } } //セントラルマネージャの状態が変化すると呼ばれる func centralManagerDidUpdateState(central: CBCentralManager!) { println("state: \(central.state)") } //周辺にあるデバイスを発見すると呼ばれる func centralManager(central: CBCentralManager!, didDiscoverPeripheral peripheral: CBPeripheral!, advertisementData: [NSObject : AnyObject]!, RSSI: NSNumber!) { println("発見したBLEデバイス: \(peripheral)") self.peripheral = peripheral self.peripheral = peripheral // 接続開始 self.centralManager.connectPeripheral(self.peripheral, options: nil) } // ペリフェラルへの接続が成功すると呼ばれる func centralManager(central: CBCentralManager!, didConnectPeripheral peripheral: CBPeripheral!) { println("接続成功!") // サービス探索結果を受け取るためにデリゲートをセット peripheral.delegate = self // サービス探索開始 peripheral.discoverServices(nil) } // ペリフェラルへの接続が失敗すると呼ばれる func centralManager(central: CBCentralManager!, didFailToConnectPeripheral peripheral: CBPeripheral!, error: NSError!) { println("接続失敗・・・") } func centralManager(central: CBCentralManager!,didDisconnectPeripheral peripheral:CBPeripheral!,error: NSError!) { println("接続が切断されました。") if error != nil { println("エラー: \(error)") } self.peripheral = nil; self.characteristic = nil; self.valueLabel.text = nil; } // サービス発見時に呼ばれる func peripheral(peripheral: CBPeripheral!, didDiscoverServices error: NSError!) { if error != nil { println("エラー: \(error)") return } let services: NSArray = peripheral.services println("\(services.count) 個のサービスを発見! \(services)") for obj in services { if let service = obj as? CBService { // キャラクタリスティック探索開始 peripheral.discoverCharacteristics(nil, forService: service) } } } // キャラクタリスティック発見時に呼ばれる func peripheral(peripheral: CBPeripheral!, didDiscoverCharacteristicsForService service: CBService!, error: NSError!) { if error != nil { println("エラー: \(error)") return } let characteristics: NSArray = service.characteristics println("\(characteristics.count) 個のキャラクタリスティックを発見! \(characteristics)") // 特定のキャラクタリスティックをプロパティに保持 let uuid = CBUUID(string:"0001") for aCharacteristic in characteristics { if aCharacteristic.UUID == uuid { self.characteristic = aCharacteristic as! CBCharacteristic break; } } } // Notify開始/停止時に呼ばれる func peripheral(peripheral: CBPeripheral!, didUpdateNotificationStateForCharacteristic characteristic:CBCharacteristic!, error: NSError!) { if error != nil { println("Notify状態更新失敗...error:%@", error) } else { println("Notify状態更新成功!characteristic UUID:\(characteristic.UUID), isNotifying: \(characteristic.isNotifying)") } } // データ更新時に呼ばれる func peripheral(peripheral: CBPeripheral!, didUpdateValueForCharacteristic characteristic: CBCharacteristic!, error: NSError!) { if error != nil { println("データ更新通知エラー:%@", error) return } else { println("データ更新! characteristic UUID: \(characteristic.UUID), value: \(characteristic.value)") updateValue() } } func peripheral(peripheral: CBPeripheral!, didWriteValueForCharacteristic characteristic: CBCharacteristic!, error: NSError!) { if error != nil { println("Write失敗...error:%@", error) } else { println("Write成功!") // このキャラクタリスティックの値には実はまだ更新が反映されていない updateValue() } } @IBAction func scanBtn(sender: UIButton) { if !isScanning { isScanning = true // スキャン開始(特定サービスを持つペリフェラルに限定) let serviceUUIDs:[AnyObject] = [CBUUID(string:"0000")] self.centralManager.scanForPeripheralsWithServices(serviceUUIDs, options: nil) sender.setTitle("STOP SCAN", forState: UIControlState.Normal) } else { // スキャン停止 self.centralManager.stopScan() sender.setTitle("START SCAN", forState: UIControlState.Normal) isScanning = false } } @IBAction func writeBtnTapped(sender: UIButton) { //13の値を送ったとき var valuemoto2 = 13 var value: UInt8 = UInt8(valuemoto2 & 0xFF) var data = NSData(bytes: [value] as [UInt8], length: 1) peripheral .writeValue(data, forCharacteristic: characteristic, type: CBCharacteristicWriteType.WithResponse) } @IBAction func notifyBtn1(sender: UIButton) { if !self.characteristic.isNotifying { // Notify開始をリクエスト self.peripheral .setNotifyValue(true, forCharacteristic: self.characteristic) sender.setTitle("STOP NOTIFY",forState:UIControlState.Normal) } else { // Notify停止をリクエスト self.peripheral .setNotifyValue(false, forCharacteristic: self.characteristic) sender.setTitle("START NOTIFY",forState:UIControlState.Normal) } } }