iOS 低功耗藍(lán)牙(BLE)的IoT應(yīng)用之 - 溫度檢測(cè)儀(上)

背景知識(shí)

藍(lán)牙技術(shù)最初由愛(ài)立信(也就是多年前手機(jī)做得最丑最奇葩的公司,最終被用戶以腳投票踢出市場(chǎng))創(chuàng)制。技術(shù)始于愛(ài)立信公司的1994方案,它是研究在移動(dòng)電話和其他配件間進(jìn)行低功耗、低成本無(wú)線通信連接的方法。發(fā)明者希望為設(shè)備間的通訊創(chuàng)造一組統(tǒng)一規(guī)則(標(biāo)準(zhǔn)化協(xié)議),以解決用戶間互不兼容的移動(dòng)電子設(shè)備。1997年前愛(ài)立信公司以此概念接觸了移動(dòng)設(shè)備制造商,討論其項(xiàng)目合作發(fā)展,結(jié)果獲得支持。

1998年5月20日,索尼愛(ài)立信、國(guó)際商業(yè)機(jī)器、諾基亞等業(yè)界龍頭創(chuàng)立“特別興趣小組”(Special Interest Group,SIG),即[藍(lán)牙技術(shù)聯(lián)盟的前身,目標(biāo)是開(kāi)發(fā)一個(gè)成本低、效益高、可以在短距離范圍內(nèi)隨意無(wú)線連接的藍(lán)牙技術(shù)標(biāo)準(zhǔn)。

這項(xiàng)無(wú)線技術(shù)的名稱取自古代丹麥維京國(guó)王Harald Bl?tand的名字,他以統(tǒng)一了因宗教戰(zhàn)爭(zhēng)和領(lǐng)土爭(zhēng)議而分裂的挪威與丹麥而聞名于世,而這個(gè)名字的英文便是Harald Bluetooth。

1998年時(shí)藍(lán)牙推出0.7規(guī)格,支持Baseband與LMP(Link Manager Protocol)通訊協(xié)定兩部分。1999年推出先后0.8版,0.9版、1.0 Draft版,1.0a版、1.0B版。1.0 Draft版,完成SDP(Service Discovery Protocol)協(xié)定、TCS(Telephony Control Specification)協(xié)定。1999年7月26日正式公布1.0版,確定使用2.4GHz頻譜,最高資料傳輸速度1Mbps,同時(shí)開(kāi)始了大規(guī)模宣傳。和當(dāng)時(shí)流行的紅外線技術(shù)相比,藍(lán)牙有著更高的傳輸速度,而且不需要像紅外線那樣進(jìn)行接口對(duì)接口的連接,所有藍(lán)牙設(shè)備基本上只要在有效通訊范圍內(nèi)使用,就可以進(jìn)行隨時(shí)連接。

當(dāng)1.0規(guī)格推出以后,藍(lán)牙并未立即受到廣泛的應(yīng)用,除了當(dāng)時(shí)對(duì)應(yīng)藍(lán)牙功能的電子設(shè)備種類少,藍(lán)牙裝置也十分昂貴。2001年的1.1版正式列入IEEE標(biāo)準(zhǔn),Bluetooth 1.1即為IEEE 802.15.1。同年,SIG成員公司超過(guò)2000家。過(guò)了幾年之后,采用藍(lán)牙技術(shù)的電子裝置如雨后春筍般增加,售價(jià)也大幅下降。為了擴(kuò)寬藍(lán)牙的應(yīng)用層面和傳輸速度,SIG先后推出了1.2、2.0版,以及其他附加新功能,例如EDR(Enhanced Data Rate,配合2.0的技術(shù)標(biāo)準(zhǔn),將最大傳輸速度提高到3Mbps)、A2DP(Advanced Audio Distribution Profile,一個(gè)控音軌分配技術(shù),主要應(yīng)用于立體聲耳機(jī)、AVRCP(A/V Remote Control Profile)等。Bluetooth 2.0將傳輸率提升至2Mbps、3Mbps,遠(yuǎn)大于1.x版的1Mbps(實(shí)際約723.2kbps)。

藍(lán)牙從1.0到2.0其速度與傳輸距離一直是其硬傷,使用體驗(yàn)極差文件型的數(shù)據(jù)傳輸幾乎等于不可用,所以我一直對(duì)藍(lán)牙設(shè)備不感冒。直至遇到BLE的出現(xiàn)也就是低功耗藍(lán)牙,來(lái)看看4.0之后的藍(lán)牙有何技術(shù)特性吧:

藍(lán)牙4.0

藍(lán)牙4.0是Bluetooth SIG于2010年7月7日推出的新的規(guī)范。其最重要的特性是支持省電

  • Bluetooth 4.0,協(xié)議組成和當(dāng)前主流的Bluetooth h2.x+EDR、還未普及的Bluetooth h3.0+HS不同,Bluetooth 4.0是Bluetooth從誕生至今唯一的一個(gè)綜合協(xié)議規(guī)范,
    還提出了“低功耗藍(lán)牙”、“傳統(tǒng)藍(lán)牙”和“高速藍(lán)牙”三種模式。
  • 其中:高速藍(lán)牙主攻數(shù)據(jù)交換與傳輸;傳統(tǒng)藍(lán)牙則以信息溝通、設(shè)備連接為重點(diǎn);藍(lán)牙低功耗顧名思義,以不需占用太多帶寬的設(shè)備連接為主。前身其實(shí)是NOKIA開(kāi)發(fā)的Wibree技術(shù),本是作為一項(xiàng)專為移動(dòng)設(shè)備開(kāi)發(fā)的極低功耗的移動(dòng)無(wú)線通信技術(shù),在被SIG接納并規(guī)范化之后重命名為Bluetooth Low Energy(后簡(jiǎn)稱低功耗藍(lán)牙)。這三種協(xié)議規(guī)范還能夠互相組合搭配、從而實(shí)現(xiàn)更廣泛的應(yīng)用模式,此外,Bluetooth 4.0還把藍(lán)牙的傳輸距離提升到100米以上(低功耗模式條件下)。
  • 分Single mode與Dual mode。
  • Single mode只能與BT4.0互相傳輸無(wú)法向下兼容(與3.0/2.1/2.0無(wú)法相通);Dual mode可以向下兼容可與BT4.0傳輸也可以跟3.0/2.1/2.0傳輸
  • 超低的峰值、平均和待機(jī)模式功耗,覆蓋范圍增強(qiáng),最大范圍可超過(guò)100米。
  • 速度:支持1Mbps數(shù)據(jù)傳輸率下的超短數(shù)據(jù)包,最少8個(gè)八組位,最多27個(gè)。所有連接都使用藍(lán)牙2.1加入的減速呼吸模式(sniff subrating)來(lái)達(dá)到超低工作循環(huán)。
  • 跳頻:使用所有藍(lán)牙規(guī)范版本通用的自適應(yīng)跳頻,最大程度地減少和其他2.4 GHz ISM頻段無(wú)線技術(shù)的串?dāng)_。
  • 主控制:可以休眠更長(zhǎng)時(shí)間,只在需要執(zhí)行動(dòng)作的時(shí)候才喚醒。
  • 延遲:最短可在3毫秒內(nèi)完成連接設(shè)置并開(kāi)始傳輸數(shù)據(jù)。
  • 健壯性:所有數(shù)據(jù)包都使用24-bit CRC校驗(yàn),確保最大程度抵御干擾。
  • 安全:使用AES-128 CCM加密算法進(jìn)行數(shù)據(jù)包加密和認(rèn)證。
  • 拓?fù)洌好總€(gè)數(shù)據(jù)包的每次接收都使用32位尋址,理論上可連接數(shù)十億設(shè)備;針對(duì)一對(duì)一連接最優(yōu)化,并支持星形拓?fù)涞囊粚?duì)多連接;使用快速連接和斷開(kāi),數(shù)據(jù)可以在網(wǎng)狀拓?fù)鋬?nèi)轉(zhuǎn)移而無(wú)需維持復(fù)雜的網(wǎng)狀網(wǎng)絡(luò)。

藍(lán)牙4.1

藍(lán)牙4.1是藍(lán)牙技術(shù)聯(lián)盟于2013年底推出的新的規(guī)范,其目的是為了讓 Bluetooth Smart 技術(shù)最終成為物聯(lián)網(wǎng)(Internet of Things)發(fā)展的核心動(dòng)力。
此版本為藍(lán)牙4.0的軟件更新版本,搭載藍(lán)牙4.0設(shè)備的終端可通過(guò)軟件更新獲得此版本。

對(duì)于開(kāi)發(fā)人員而言,該更新是藍(lán)牙技術(shù)發(fā)展史上一項(xiàng)重要的進(jìn)步。該更新提供了更高的靈活性和掌控度,讓開(kāi)發(fā)人員能創(chuàng)造更具創(chuàng)新并催化物聯(lián)網(wǎng)(IOT)發(fā)展的產(chǎn)品。
支持多設(shè)備連接。

  • 智能連接:增加設(shè)置設(shè)備間連接頻率的支持。制造商可以對(duì)設(shè)備設(shè)置連接進(jìn)行設(shè)置,使得設(shè)備可以更加智能的控制設(shè)備電量。

藍(lán)牙4.2

藍(lán)牙4.2是藍(lán)牙技術(shù)聯(lián)盟于2014年12月推出的新的規(guī)范。

藍(lán)牙5

藍(lán)牙5在2016年6月被宣布。在有效傳輸距離上將是4.2LE版本的4倍(理論上可達(dá)300米),傳輸速度將是4.2LE版本的2倍(速度上限為24Mbps)。藍(lán)牙5.0還支持室內(nèi)定位導(dǎo)航功能(結(jié)合WiFi可以實(shí)現(xiàn)精度小于1米的室內(nèi)定位),允許無(wú)需配對(duì)接受信標(biāo)的數(shù)據(jù)(比如廣告、Beacon、位置信息等,傳輸率提高了8倍),針對(duì)物聯(lián)網(wǎng)進(jìn)行了很多底層優(yōu)化。

看完這些特性除了不能上網(wǎng)以外其它的無(wú)線能力幾乎都能秒殺WIFI了。

藍(lán)牙常見(jiàn)名稱和縮寫(xiě)

在進(jìn)入IOS之前我們得深入到的理論領(lǐng)域,了解藍(lán)牙4.0中的一些基本的術(shù)語(yǔ):

  • MFI:make for ipad, iphone, itouch 專們?yōu)樘O(píng)果設(shè)備制作的設(shè)備
  • BLE:buletouch low energy,藍(lán)牙4.0設(shè)備因?yàn)榈秃碾?,所以也叫做BLE
  • peripheral:外設(shè),被連接的設(shè)備
  • central:中心,發(fā)起連接的時(shí)central
  • service:服務(wù),每個(gè)外設(shè)會(huì)有很多服務(wù),每個(gè)服務(wù)中包含很多字段,這些字段的權(quán)限一般分為 讀read,寫(xiě)write,通知notiy幾種,就是我們連接設(shè)備后具體需要操作的內(nèi)容。
  • characteristic:特征 每個(gè)設(shè)備會(huì)提供服務(wù)和特征,類似于服務(wù)端的api,但是機(jī)構(gòu)不同。
  • Description:每個(gè)characteristic可以對(duì)應(yīng)一個(gè)或多個(gè)Description用戶描述characteristic的信息或?qū)傩?br> 外設(shè)、服務(wù)、特征間的關(guān)系

外設(shè)、服務(wù)、特征間的關(guān)系

下圖你在Apple的開(kāi)發(fā)者網(wǎng)站上也能找到:

外設(shè)、服務(wù)、特征間的關(guān)系

藍(lán)牙中心模式流程

  1. 建立中心角色
  2. 掃描外設(shè)(discover)
  3. 連接外設(shè)(connect)
  4. 掃描外設(shè)中的服務(wù)和特征(discover)
    4.1 獲取外設(shè)的services
    4.2 獲取外設(shè)的Characteristics,獲取Characteristics的值,獲取Characteristics的Descriptor和Descriptor的值
  5. 與外設(shè)做數(shù)據(jù)交互(explore and interact)
  6. 訂閱Characteristic的通知
  7. 斷開(kāi)連接(disconnect)

藍(lán)牙設(shè)備工作的狀態(tài)

  • 準(zhǔn)備(standby)
  • 廣播(advertising)
  • 監(jiān)聽(tīng)掃描(Scanning
  • 發(fā)起連接(Initiating)
  • 已連接(Connected)

藍(lán)牙和版本的使用限制

  • 藍(lán)牙2.0:越獄設(shè)備
  • 藍(lán)牙4.0:IOS6 以上
  • MFI認(rèn)證設(shè)備(Make For ipod/ipad/iphone):無(wú)限制

iOS 的實(shí)現(xiàn)

先創(chuàng)建一個(gè) Swift 的工程,然后引入CoreBluetooth ,它就是iOS中提供藍(lán)牙通信的核心庫(kù)。然后ViewController必須實(shí)現(xiàn) CBCentralManagerDelegate,CBPeripheralDelegate 兩個(gè)接口:

import CoreBluetooth
import UIKit

class ViewController: UIViewController, CBCentralManagerDelegate,CBPeripheralDelegate {
        override func viewDidLoad() {
            super.viewDidLoad()
        }
} 

1.) 建立中心角色

然后要定義一個(gè)管理中心的對(duì)象變量,它就是藍(lán)牙控制的主入口,然后在viewDidLoad()中實(shí)例化它:

class ViewController: UIViewController, CBCentralManagerDelegate,CBPeripheralDelegate {
    var manager : CBCentralManager!        
    override func viewDidLoad() {
            super.viewDidLoad()
            manager = CBCentralManager.init(delegate: self, queue: DispatchQueue.main)
        }
} 

2.) 掃描外設(shè)

管理中心一但被初始化后就會(huì)檢查當(dāng)前手機(jī)上的藍(lán)牙狀態(tài),并調(diào)用 centralManagerDidUpdateState 方法,假設(shè)完整初始化后就馬上進(jìn)行設(shè)備掃描,那就要在centralManagerDidUpdateState中進(jìn)行,在 viewDidLoad下方加入以下的代碼:

 func centralManagerDidUpdateState(_ central: CBCentralManager) {
        switch central.state {
        case .unknown:
            print("未知的藍(lán)牙設(shè)備")
        case .resetting:
            print("藍(lán)牙正在被重置中")
        case .unsupported:
            print("不支持藍(lán)牙服務(wù)")
        case .unauthorized:
            print("藍(lán)牙服務(wù)未被授權(quán)")
        case .poweredOff:
            print("藍(lán)牙服務(wù)已關(guān)閉")
        case .poweredOn:
            print("藍(lán)牙已啟動(dòng),開(kāi)始掃描...")
            startScan()
        }
    }

 /// 掃描藍(lán)牙設(shè)備
  func startScan() {
        manager.scanForPeripherals(withServices: nil, options: nil)
  }

注:只有返回.poweredOn狀態(tài)下才能進(jìn)行藍(lán)牙設(shè)備的掃描。

scanForPeripherals方法就是對(duì)可發(fā)現(xiàn)的藍(lán)牙設(shè)備進(jìn)行掃描,輸入的參數(shù)可以作一些過(guò)濾條件,我這里是不加任何的過(guò)濾條件無(wú)差別化地掃描。

3.) 連接外設(shè)

當(dāng)scanForPeripherals方法被調(diào)用后,CoreBluetooth就會(huì)調(diào)用centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber),所以我們就得在這個(gè)方法內(nèi)將我們的目標(biāo)藍(lán)牙設(shè)備找出來(lái),然后用一個(gè)變量Hold住它,否則這個(gè)外設(shè)就會(huì)被釋放掉,然后你就會(huì)在XCode中得到一條提示信息說(shuō)你沒(méi)有Hold住你需要的設(shè)備變量了。

// 定義一個(gè)變量來(lái)Hold住目標(biāo)設(shè)備
var connectedPeripheral : CBPeripheral!

func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
            if (peripheral.name=="BT05") {
                manager.connect(peripheral, options:nil)
                // 找到的設(shè)備必須持有它,否則CBCentralManager中也不會(huì)保存peripheral,那么CBPeripheralDelegate中的方法也不會(huì)被調(diào)用!
                connectedPeripheral = peripheral
                print("正在連接:\(peripheral.name)...")
            }
}

由于我不知道我的設(shè)備的ServiceUUID是什么,為了方便我編碼的需要所以我只找一個(gè)名為"BT05"的設(shè)備名稱(這是我藍(lán)牙模塊的默認(rèn)名字)找到之后就馬上調(diào)用connect進(jìn)行連接,注意:當(dāng)中心對(duì)象不停止掃描(manager.stopScan())以上的方法就會(huì)不斷地被調(diào)用

一個(gè)主設(shè)備最多能連7個(gè)外設(shè),每個(gè)外設(shè)最多只能給一個(gè)主設(shè)備連接,連接成功,失敗,斷開(kāi)會(huì)進(jìn)入各自的委托

func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral)
func centralManager(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: Error?)
func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?)

下文就會(huì)補(bǔ)充這幾個(gè)委托方法的實(shí)現(xiàn)

4.1) 獲取外設(shè)的服務(wù)

當(dāng)連接成功后 centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) 就會(huì)被調(diào)用,在這里我們會(huì)設(shè)置找到的外設(shè)對(duì)象的委托(self),通常這個(gè)方法內(nèi)會(huì)做另一種篩選那就是服務(wù)特征,通俗點(diǎn)說(shuō)就是這個(gè)藍(lán)牙設(shè)備可以提供些啥服務(wù),例如寫(xiě)入,讀取,或者其它什么的一些動(dòng)作。

    func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral){        
        print("正在查找Service")
        // 外設(shè)尋找目標(biāo)服務(wù)
        peripheral.discoverServices(nil)
        peripheral.delegate = self
        self.title = peripheral.name
        // 停止掃描
        manager.stopScan()
    }

4.2) 獲取外設(shè)特征

discoverServices(nil)傳入了nil 那么就啥服務(wù)信息都直接獲取,然后對(duì)服務(wù)進(jìn)行發(fā)現(xiàn),這個(gè)過(guò)程和前文中的設(shè)備發(fā)現(xiàn)非常的像

    // 這個(gè)服務(wù)地址可以從設(shè)備中查到的
    let ServiceUUID =  "FFE0"
    //掃描到Services
    func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?){
        if (error != nil){
            print("查找 services 時(shí) \(peripheral.name) 報(bào)錯(cuò) \(error?.localizedDescription)")
        }
        
        for service in peripheral.services! {
            // 需要連接的 CBCharacteristic 的 UUID
            if service.uuid.uuidString == ServiceUUID {
                peripheral.discoverCharacteristics(nil, for: service)
            }
        }
    }

5) 與外設(shè)做數(shù)據(jù)交互

這樣我們實(shí)現(xiàn)的CBPeripheralDelegate委托接口中的peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?)將會(huì)被調(diào)用,用于讀取藍(lán)牙傳過(guò)來(lái)的數(shù)據(jù)。

   // 同理我們要Hold住特征變量,否則會(huì)被釋放掉
    var savedCharacteristic : CBCharacteristic!
    var lastString : NSString!

   // Interface Build 中的標(biāo)簽對(duì)象,用來(lái)顯示從傳感器讀取的室溫
   @IBOutlet weak var ?lbTemperature: UILabel!
    //獲取的charateristic的值,處理收接到的數(shù)據(jù)
    func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
        // 好了,charcteristic.value 就是從藍(lán)牙設(shè)備傳過(guò)來(lái)的原數(shù)據(jù)內(nèi)容,我們可以在這里進(jìn)行處理
        let resultStr = NSString(data: characteristic.value!, encoding: String.Encoding.utf8.rawValue)
        print(resultStr)
        
        // 將溫度顯示到標(biāo)簽中
        lbTemperature.text = resultStr
        
        if lastString == resultStr {
            return;
        }

        self.savedCharacteristic = characteristic
    }

6) 訂閱 Characteristic 的通知

由于BLE4.0的"減弱式呼吸"工作特性我們需要對(duì)外設(shè)置發(fā)出的通知進(jìn)行訂閱,如數(shù)據(jù)發(fā)生改變中心可以第一時(shí)間獲知。

    /// 掃描到 characteristic 時(shí)
    func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error? {
        if error != nil {
            print("查找 characteristics 時(shí) \(peripheral.name) 報(bào)錯(cuò) \(error?.localizedDescription)")
        }
        
        //獲取Characteristic的值,讀到數(shù)據(jù)會(huì)進(jìn)入方法:
        for characteristic in service.characteristics! {
            peripheral.readValue(for: characteristic)
            //設(shè)置 characteristic 的 notifying 屬性 為 true , 表示接受廣播
            peripheral.setNotifyValue(true, for: characteristic)
        }
    }

7) 斷開(kāi)連接

其實(shí)到此這個(gè)App就完成了,以下的這些代碼是對(duì)相關(guān)的錯(cuò)誤處理進(jìn)行補(bǔ)全:

    /// 連接到Peripherals-失敗
    func centralManager(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: Error?){
        print("連接到名字為 \(peripheral.name) 的設(shè)備失敗,原因是 \(error?.localizedDescription)")
        
    }
    
    /// 斷開(kāi)
    func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?){
        print("連接到名字為 \(peripheral.name) 的設(shè)備斷開(kāi),原因是 \(error?.localizedDescription)"
        let alertView = UIAlertController.init(title: "抱歉", message: "藍(lán)牙設(shè)備\(peripheral.name)連接斷開(kāi),請(qǐng)重新掃描設(shè)備連接", preferredStyle: UIAlertControllerStyle.alert)
        alertView.show(self, sender: nil)
        
    }

   func peripheral(_ peripheral: CBPeripheral, didWriteValueFor characteristic: CBCharacteristic, error: Error?){
        if error != nil{
            print("寫(xiě)入 characteristics 時(shí) \(peripheral.name) 報(bào)錯(cuò) \(error?.localizedDescription)")
        }
        // 這里可以處理向設(shè)備寫(xiě)入數(shù)據(jù)時(shí)的回調(diào)
    }

在下一篇中將會(huì)介紹藍(lán)牙設(shè)備與Arduino 一端關(guān)于硬件部分與固件部分的制作。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容

  • 藍(lán)牙簡(jiǎn)介 藍(lán)牙( Bluetooth? ):是一種無(wú)線技術(shù)標(biāo)準(zhǔn),可實(shí)現(xiàn)固定設(shè)備、移動(dòng)設(shè)備和樓宇個(gè)人域網(wǎng)之間的短距離...
    Chefil閱讀 2,070評(píng)論 2 19
  • 首先進(jìn)一則廣告: 藍(lán)牙技術(shù)聯(lián)盟(Bluetooth SIG)2010年7月7日宣布,正式采納藍(lán)牙4.0核心規(guī)范(B...
    L澤閱讀 1,472評(píng)論 3 4
  • 由于最近工作的東家是一家物聯(lián)網(wǎng)公司,網(wǎng)上BLE相關(guān)的資料確實(shí)比較少,尤其我還做了一些調(diào)試和加密相關(guān)的工作.由于調(diào)試...
    陳長(zhǎng)見(jiàn)閱讀 2,079評(píng)論 5 11
  • 由于最近咨詢藍(lán)牙問(wèn)題的較多,所以在此總結(jié)一下!馬尾哥的第一個(gè)完整項(xiàng)目就是藍(lán)牙與外設(shè)交互的項(xiàng)目!考慮到功耗的因素現(xiàn)在...
    zhangyajie閱讀 1,024評(píng)論 0 4
  • 彭京豪 初次相遇皮影戲,是我幼少時(shí)候在一個(gè)夕陽(yáng)共霞光燦爛的傍晚。 古巷,青藤,鳥(niǎo)鳴。 ...
    星月無(wú)痕520閱讀 880評(píng)論 0 5