fb-script

2015年10月12日 星期一

iOS CoreBluetooth swift 2 連線客製化藍芽 BLE 模組 - ( 1 ) 掃描

1.先拉一個 UIViewController 並用 NavigationController embed 起來,在 Navigation Bar 上面加個 title 和 UIBarButtonItem,這邊是選擇 Refresh 的 System Item 樣式,並且在剩下的範圍加上一個 UITableview 當作掃描後顯示藍芽裝置的列表

2.製作一個客製化的 UITableviewCell 讓掃描結束後可以顯示資訊
Name:藍芽裝置的名稱,通常是我們是可以知道的
RSSI:藍芽裝置的訊號強度,這個執事參考值,會受到周遭的環境影響
UUID:藍芽裝置的唯一 ID
Connectable:該藍芽裝置是否可以被連線
Distance:目前與該藍芽裝置的距離,這個值是用 RSSI 算出來的,但是因為 RSSI 的環境參數常常都不同,所以只能當參考。( 個人試過有時候會差很多,網上參考資源很多這邊也不對轉換公式做贅述 )
簡單的 UITableviewCell
好啦真的有點醜,不過測試用就別計較啦

3.東西準備好了那要來掃描了,首先這邊使用 CoreBlueTooth 這個函式庫。要進行所有藍芽工作時,都要從一個 CBCentralManager 物件來開始動作。方法很簡單,在 UIViewcontroller 中建立一個全域物件,並且在會執行到的地方來初始化他

var myCenteralManager: CBCentralManager? = nil

override func viewWillAppear(animated: Bool) {
    myCenteralManager = CBCentralManager(delegate: self, queue: nil)
}

這邊會發現他的 delegate 放的是 self,也就是 UIViewController 的實體,所以我們的 UIViewController 需要實作 CBCentralManagerDelegate 的委派,其實就是要實作 centralManagerDidUpdateState 這個方法啦~

在這個方法中會隨時監聽 iOS 裝置的藍芽狀態,只要有改變就會進行 callback 的動作,其中動作有分 5 項,這邊就通通 print 出來就好

func centralManagerDidUpdateState(central: CBCentralManager) {
        switch central.state {
        case CBCentralManagerState.PoweredOn:
            print("BT ON")
        case CBCentralManagerState.PoweredOff:
            print("BT OFF")
        case CBCentralManagerState.Resetting:
            print("BT RESSTING")
        case CBCentralManagerState.Unknown:
            print("BT UNKNOW")
        case CBCentralManagerState.Unauthorized:
            print("BT UNAUTHORIZED")
        case CBCentralManagerState.Unsupported:
            print("BT UNSUPPORTED")
        }
    }
接下來就把 UIBarButtonItemtouchUpInside 事件去執行掃描的動作
不要重複掃描:當開始掃描後會把 UIBarButtonItem 的 Enable 屬性改為 false
不要一直掃描:一直掃描會非常耗電,所以盡量設一個掃描時間

啟動掃描後當偵測到藍芽裝置就會進入進入didDiscoverPeripheralcallBack,所以這邊先用幾個陣列才儲存掃瞄到的資訊

var BTPeripheral:[CBPeripheral] = [] //  儲存掃瞄到的 peripheral 物件
var BTIsConnectable: [Int] = [] //  儲存各個藍芽裝置是否可連線
var BTRSSI:[NSNumber] = [] // 儲存各個藍芽裝置的訊號強度

好那就開始進行掃描吧

@IBAction func actionScan(sender: UIBarButtonItem!) {
    bbScan.enabled = false
    bbScan.title = "Scanning"
    BTPeripheral.removeAll(keepCapacity: false)
    BTRSSI.removeAll(keepCapacity: false)
    BTIsConnectable.removeAll(keepCapacity: false)
    myCenteralManager!.scanForPeripheralsWithServices(nil, options: nil)
    NSTimer.scheduledTimerWithTimeInterval(5, target: self, selector: "stopScan", userInfo: nil, repeats: false)
}

func stopScan() {
    tbvBTDevices.reloadData()
    myCenteralManager!.stopScan()
    bbScan.title = "Scan"
    bbScan.enabled = true
}

點擊掃描案的時後以防重複掃描所以先把按鈕狀態改成 Disable,因為重新掃描了所以將儲存的陣列清空,然後就執行 scanForPeripheralWithServices 開始掃描了,這個方法所帶入的參數是可以指定搜尋條件的。不過這邊先是把所有的裝置都搜尋出來,最後設定 5 秒後停止掃描,並且把掃描的按鈕狀態改回 Enable

4.開啟掃描後只要有掃描到裝置就會進入centralManagerdidDiscoverPeripheral callback 方法,所以要在此方法中將收到的資料儲存到剛剛建立的幾個陣列中,並且 reload UITableview 讓他即時更新

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

    let temp = BTPeripheral.filter { (pl) -> Bool in
        return pl.name == peripheral.name
    }

    if temp.count == 0 {
        BTPeripheral.append(peripheral)
        BTRSSI.append(RSSI)
        BTIsConnectable.append(Int(advertisementData[CBAdvertisementDataIsConnectable]!.description)!)        
    }
    tbvBTDevices.reloadData()
}

這邊先判定了是否陣列已經有該筆紀錄,將沒有的儲存起來,可以看到 Connectable 的資訊是存在 advertisementData 之中,最後在 tableview 的顯示就放在 cellForRowAtIndexPath 方法中

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCellWithIdentifier("BlueToothTableViewCell", forIndexPath: indexPath) as! BlueToothTableViewCell

    cell.lbName.text = BTPeripheral[indexPath.row].name
    cell.lbID.text = BTPeripheral[indexPath.row].identifier.UUIDString
    cell.lbRSSI.text = "\(BTRSSI[indexPath.row])"
    let distancePower = Double(abs(BTRSSI[indexPath.row].integerValue) - RSSI_MEAN) / Double(10 * RSSI_N)
    cell.lbDistance.text = "\(pow(10.0,distancePower)) M"
    cell.lbIsConnectable.text = BTIsConnectable[indexPath.row].description
    return cell
}

其中的 Distance 目前是設定 RSSI_MEAN 是 70,RSSI_N 是 1 的環境條件下去做計算,當然只能參考了

掃描後的畫面像這樣,其中第一和第二個就是我要連的客製化藍芽模組,第三個是我的藍芽滑鼠哈哈

下一步就是進行連線囉

上一篇 連線客製化藍芽 BLE 模組 - ( 0 ) 大綱

下一篇 連線客製化藍芽 BLE 模組 - ( 2 ) 連線

3 則留言:

  1. 作者已經移除這則留言。

    回覆刪除
    回覆
    1. 最後一篇有範例喔

      http://shenyun23-4.blogspot.tw/2015/11/ios-corebluetooth-swift-2-ble-4.html

      刪除