如何使用LinkIt 7697開發板的BLE藍牙功能?

本文要介紹聯發科技實驗室7697開發板的BLE藍牙4.0通訊機制,包含central、iBeacon 以及 peripheral 等三種角色。

作者/攝影  曾吉弘
時間   3小時
難度  🌟🌟🌟
材料表

LinkIt 7697的BLE

LinkIt 7697支援藍牙低功耗通訊協定Bluetooth Low Energy),但不支援傳統的 profile 像是 Bluetooth BR 或 EDR。Bluetooth Low Energy 裝置可作為 central 或 peripheral 裝置。peripheral裝置(像是心跳計) 會把關於本身的資料廣播出去。central裝置則會尋找附近的peripheral裝置,並可進一步連到peripheral 來存取其屬性,例如服務(service)或特徵(chracteristic)。

*BLE peripheral 與 Central   001

LinkIt 7697 提供了以下 Arduino 函式庫類別:

  • LBLE:用來初始化 Bluetooth Low Energy子系統subsystem
  • LBLECentral:提供central裝置的基本功能,例如掃描鄰近的peripheral
  • LBLEPeripheral:提供peripheral裝置的基本功能:
    • 設定advertisement package
    • 設定 characteristic與service(UUID).

7697 可用作 Central、iBeacon 以及 peripheral,請看以下介紹:

 

7697作為Central

7697的LBLE 函式庫可讓 7697 成為一個central裝置來掃描附近的BLE裝置。如以下程式中的scanner

 

[pastacode lang=”java” manual=”LBLE.begin()%3B%0Awhile(!LBLE.ready())%0A%7B%0Adelay(10)%3B%0A%7D%0ASerial.println(%22BLE%20ready%22)%3B%0A%0A%2F%2F%E9%96%8B%E5%A7%8B%E6%8E%83%E7%9E%84%E9%84%B0%E8%BF%91%E7%9A%84%E5%BB%A3%E6%92%AD%E8%B3%87%E6%96%99%0ALBLECentral%20scanner%3B%0Ascanner.scan()%3B%0A%0A%E5%91%BC%E5%8F%ABscan()%E4%B9%8B%E5%BE%8C%EF%BC%8Ccentral%E8%A3%9D%E7%BD%AE%E5%B0%B1%E6%9C%83%E4%B8%8D%E6%96%B7%E8%92%90%E5%B0%8B%E9%99%84%E8%BF%91%E7%9A%84Blueotooth%E8%A3%9D%E7%BD%AE%EF%BC%8C%E6%8E%83%E6%8F%8F%E7%B5%90%E6%9E%9C%E6%9C%83%E6%9B%B4%E6%96%B0%E5%9C%A8LBLECentral%E9%A1%9E%E5%88%A5%EF%BC%8C%E5%BE%8C%E7%BA%8C%E5%8F%AF%E5%91%BC%E5%8F%AB%20getPeripheralCount()%20%E4%BE%86%E5%8F%96%E5%BE%97%E6%8E%83%E7%9E%84%E5%88%B0%E7%9A%84%20peripheral%20%E8%A3%9D%E7%BD%AE%E6%95%B8%E9%87%8F%E3%80%82%0A%0Afor(int%20i%20%3D%200%3B%20i%20%3C%20scanner.getPeripheralCount()%3B%20%2B%2Bi)%0A%7B%0ASerial.println(scanner.getName(i))%3B%0A%7D” message=”” highlight=”” provider=”manual”/]

請注意藍牙 peripheral 的廣播內容會因不同的裝置而異,例如,一台 iBeacon 裝置丟出來資料可能會有 connectivity flag 以及製造商資料,但心跳計則可能包含 service UUID 與裝置名稱。

常見的情境是 central 去掃描附近的 iBeacon 裝置,在此會用到兩個方法isIBeacongetIBeaconInfo,用來解析iBeacon廣播資料相當好用。有掃到裝置的話,就可由Arduino IDE 的 serial monitor 畫面看到該裝置的基本資料。

 

File > Examples > LBLE > ScanPeripherals

[pastacode lang=”java” manual=”%23include%20%3CLBLE.h%3E%0A%23include%20%3CLBLECentral.h%3E%0A%0ALBLECentral%20scanner%3B%0A%0Avoid%20setup()%20%7B%0A%20%20Serial.begin(9600)%3B%0A%0A%20%20%2F%2F%E5%88%9D%E5%A7%8B%E5%8C%96BLE%0A%20%20Serial.println(%22BLE%20begin%22)%3B%0A%20%20LBLE.begin()%3B%0A%20%20while%20(!LBLE.ready())%20%7B%0A%20%20%20%20delay(10)%3B%0A%20%20%7D%0A%20%20Serial.println(%22BLE%20ready%22)%3B%0A%0A%20%20%2F%2F%E9%96%8B%E5%A7%8B%E6%8E%83%E7%9E%84%E9%84%B0%E8%BF%91%E7%9A%84%E5%BB%A3%E6%92%AD%E8%B3%87%E6%96%99%0A%20%20scanner.scan()%3B%0A%7D%0A%0Avoid%20printDeviceInfo(int%20i)%20%7B%0A%20%20Serial.print(%22Addr%3A%20%22)%3B%0A%20%20Serial.println(scanner.getAddress(i))%3B%0A%20%20Serial.print(%22RSSI%3A%20%22)%3B%0A%20%20Serial.println(scanner.getRSSI(i))%3B%0A%20%20Serial.print(%22Name%3A%20%22)%3B%0A%20%20Serial.println(scanner.getName(i))%3B%0A%20%20Serial.print(%22UUID%3A%20%22)%3B%0A%20%20if%20(!scanner.getServiceUuid(i).isEmpty())%20%7B%0A%20%20%20%20Serial.println(scanner.getServiceUuid(i))%3B%0A%20%20%7D%20else%20%7B%0A%20%20%20%20Serial.println()%3B%0A%20%20%7D%0A%20%20Serial.print(%22Flag%3A%20%22)%3B%0A%20%20Serial.println(scanner.getAdvertisementFlag(i)%2C%20HEX)%3B%0A%20%20Serial.print(%22Manu%3A%20%22)%3B%0A%20%20Serial.println(scanner.getManufacturer(i))%3B%0A%0A%20%20if%20(scanner.isIBeacon(i))%20%7B%0A%20%20%20%20LBLEUuid%20uuid%3B%0A%20%20%20%20uint16_t%20major%20%3D%200%2C%20minor%20%3D%200%3B%0A%20%20%20%20uint8_t%20txPower%20%3D%200%3B%0A%20%20%20%20scanner.getIBeaconInfo(i%2C%20uuid%2C%20major%2C%20minor%2C%20txPower)%3B%0A%0A%20%20%20%20Serial.println(%22iBeacon-%3E%22)%3B%0A%20%20%20%20Serial.print(%22%20%20%20%20UUID%3A%20%22)%3B%0A%20%20%20%20Serial.println(uuid)%3B%0A%20%20%20%20Serial.print(%22%20%20%20%20Major%3A%20%22)%3B%0A%20%20%20%20Serial.println(major)%3B%0A%20%20%20%20Serial.print(%22%20%20%20%20Minor%3A%20%22)%3B%0A%20%20%20%20Serial.println(minor)%3B%0A%20%20%20%20Serial.print(%22%20%20%20%20txPower%3A%20%22)%3B%0A%20%20%20%20Serial.println(txPower)%3B%0A%20%20%7D%0A%7D%0A%0Avoid%20loop()%20%7B%0A%20%20delay(3000)%3B%0A%0A%20%20%2F%2F%E9%A1%AF%E7%A4%BA%E6%89%BE%E5%88%B0%E7%9A%84peripheral%E8%A3%9D%E7%BD%AE%0A%20%20Serial.println(scanner.getPeripheralCount())%3B%0A%20%20for%20(int%20i%20%3D%200%3B%20i%20%3C%20scanner.getPeripheralCount()%3B%20%2B%2Bi)%20%7B%0A%20%20%20%20Serial.println(%22—%22)%3B%0A%20%20%20%20printDeviceInfo(i)%3B%0A%20%20%7D%0A%0A%20%20Serial.println(%22———————-%22)%3B%0A%7D%0A” message=”” highlight=”” provider=”manual”/]

 

7697作為iBeacon

iBeacon是一個只會廣播(advertise)資料的BLE裝置,廣播的資料格式可以自訂。請注意做為 iBeacon 的 BLE 裝置無法被連接。

請用LBLEAdvertisement類別就能產生所需的iBeacon廣播資料。並可呼叫configAsIBeacon方法將廣播資料設定為iBeacon格式,如以下程式碼所示:

LBLEAdvertisementData beaconData;
beaconData.configAsIBeacon(“E2C56DB5-DFFB-48D2-B060-D0F5A71096E0”, 01, 02, -40);

  • E2C56DB5-DFFB-48D2-B060-D0F5A71096E0是您的 iBeacon UUID。這個值您可以自行產生。E2C56DB5-DFFB-48D2-B060-D0F5A71096E0因為是iOS AirLocate iBeacon example 中所用的UUID,因此許多 iOS 工具程式都有內建設定來搜尋這組UUID。
  • 01與02是 major ID 與 minor ID,可用來區別不同的iBeacon裝置。例如,您可能會在會議室或辦公室放置多台7697。您可把 major number 設為樓層,minor number 則是房間編號等。這樣一來iBeacon scanner只要透過這兩個數字就能知道使用者現在是在哪一層樓的哪一間房間了。
  • 最後一個數字Tx Power,-40,代表該裝置的發送功率。beacon scanner只要去比較這個數值與實際的訊號強度就能約略估算距離。實際的發送功率會根據天線設定方式而有異,所以建議這個數值要與 scanner 一併校正。

廣播資料設定完成之後,只要呼叫LBLEPeripheral的 advertise方法就能開始廣播資料:

LBLEPeripheral.advertise(beaconData);
LinkIt 7697 會馬上開始廣播資料,呼叫 stopAdvertise() 即可停止廣播。

7697 有現成的範例(BeaconAdvertisement),您可運用Locate Beacon這類的 ibeacon 工具來搜尋。以下是這個 app 的搜尋畫面擷圖:

File > Examples > LBLE > BeaconAdvertisement

[pastacode lang=”markup” manual=”%23include%20%3CLBLE.h%3E%0A%23include%20%3CLBLEPeriphral.h%3E%0A%0Avoid%20setup()%20%7B%0A%20%20Serial.begin(9600)%3B%0A%0A%20%20%2F%2F%E5%88%9D%E5%A7%8B%E5%8C%96BLE%0A%20%20Serial.println(%22BLE%20begin%22)%3B%0A%20%20LBLE.begin()%3B%0A%20%20while%20(!LBLE.ready())%20%7B%0A%20%20%20%20delay(100)%3B%0A%20%20%7D%0A%20%20Serial.println(%22BLE%20ready%22)%3B%0A%0A%20%20%2F%2F%E5%B0%87%E5%BB%A3%E6%92%AD%E8%B3%87%E6%96%99%E8%A8%AD%E5%AE%9A%E7%82%BAiBeacon.%0A%20%20LBLEAdvertisementData%20beaconData%3B%0A%0A%20%20%2F%2F%E4%BB%A5%E4%B8%8B%E6%98%AFAirLocate%E9%80%9A%E7%94%A8%E7%9A%84UUID.%0A%20%20LBLEUuid%20uuid(%22E2C56DB5-DFFB-48D2-B060-D0F5A71096E0%22)%3B%0A%20%20beaconData.configAsIBeacon(uuid%2C%2001%2C%2002%2C%20-40)%3B%0A%0A%20%20Serial.print(%22Start%20advertising%20iBeacon%20with%20uuid%3D%22)%3B%0A%20%20Serial.println(uuid)%3B%0A%0A%20%20%2F%2F%E9%96%8B%E5%A7%8B%E5%BB%A3%E6%92%AD%0A%20%20LBLEPeripheral.advertise(beaconData)%3B%0A%7D%0A%0Avoid%20loop()%20%7B%0A%20%20%2F%2F%20The%20underlying%20framework%20will%20advertise%20periodically.%0A%20%20%2F%2F%20we%20simply%20wait%20here.%0A%20%20%2F%2F%20You%20can%20use%20iBeacon%20apps%20such%20as%0A%20%20%2F%2F%20%22Locate%20Beacon%22%20by%20Radius%20Networks%20on%20iOS%20devices%20to%20locate%20this%20beacon.%0A%20%20delay(3000)%3B%0A%7D%0A” message=”” highlight=”” provider=”manual”/]

7697作為peripheral

第三種應用是將 7697 作為周邊,這時我們就能從別的裝置去連接它。這也是CAVEDU之前在使用LinkIt ONE較常用的方式,搭配手機來與開發板互動。

 

peripheral device裝置上需要先建立一個 GATT 伺服器,才能讓其他的 BLE central 連到這個peripheral。GATT伺服器包含了一組屬性:service與characterstic。像是心跳帶這類的服務就是用UUID來區分。一個服務可能包含多個具有數值(value)的特徵(characteristic),例如心跳帶服務就應該會有一個heart rate measurement value特徵。BLE central裝置就可以發送需求來讀寫這些數值。

關於GATT屬性,請參考Bluetooth.com 或 https://learn.adafruit.com/introduction-to-bluetooth-low-energy/gatt#services-and-characteristics

啟動GATT伺服器之前,需要定義這個服務以及我們要用到的特徵。由於服務與特徵都是永久性(persistent),並且應該會被其他central裝置頻繁讀寫,在此把service與characteristic定義為global是比較好的做法,如下所示:

#include <LBLE.h>
#include <LBLEPeriphral.h>

//定義具備一個特徵GATT服務
LBLEService ledService(“19B10010-E8F2-537E-4F6C-D104768A1214”);
LBLECharacteristicInt switchCharacteristic(“19B10011-E8F2-537E-4F6C-D104768A1214”, LBLE_READ | LBLE_WRITE);

  • 19B10010-E8F2-537E-4F6C-D104768A1214是服務UUID,可供其他central裝置尋找到它。
  • 19B10011-E8F2-537E-4F6C-D104768A1214是特徵UUID。. GATT通訊協定會把特徵視為一個緩衝器,長度最大可到512位元組。 LBLECharacteristicInt可用來定義整數型別的特徵,您再用setValue() 去設定其整數參數即可,不必去動到緩衝器內部。

定義好服務與特徵之後,接著要定義它們的關係,再把這些服務加入peripheral裝置,如下所示:

void setup() {
//將特徵加入ledService
ledService.addAttribute(switchCharacteristic);

//將服務加入GATT伺服器(即一個 BLE peripheral)
LBLEPeripheral.addService(ledService);
使用以下語法來初始化GATT伺服器:

LBLEPeripheral.begin();
請注意一旦呼叫begin()之後,就無法加入更多service,也無法修改service與characteristic之間的關係了。

peripheral裝置現在已準備好由central裝置來連線,並可藉由isWritten()檢查特徵值是否有被外部裝置來修改:

 

File > Examples > LBLE > SimplePeripheral

 

為了安全性起見,裝置的addr 不會直接寫在板子上(當然也是有這麼做的)。本範例透過以下語法將 7697 addr 顯示於 serial monitor 中,您再修改 App Inventor 即可。

 

Serial.println(LBLE.getDeviceAddress());

 

[pastacode lang=”java” manual=”%23include%20%3CLBLE.h%3E%0A%23include%20%3CLBLEPeriphral.h%3E%0A%0A%2F%2F%20Define%20a%20simple%20GATT%20service%20with%20only%201%20characteristic%0ALBLEService%20ledService(%2219B10010-E8F2-537E-4F6C-D104768A1214%22)%3B%0ALBLECharacteristicInt%20switchCharacteristic(%2219B10011-E8F2-537E-4F6C-D104768A1214%22%2C%20LBLE_READ%20%7C%20LBLE_WRITE)%3B%0A%0Avoid%20setup()%20%7B%0A%20%20pinMode(LED_BUILTIN%2C%20OUTPUT)%3B%0A%20%20digitalWrite(LED_BUILTIN%2C%20LOW)%3B%0A%0A%20%20Serial.begin(9600)%3B%0A%0A%20%20%2F%2F%E5%88%9D%E5%A7%8B%E5%8C%96BLE%0A%20%20Serial.println(%22BLE%20begin%22)%3B%0A%20%20LBLE.begin()%3B%0A%20%20while%20(!LBLE.ready())%20%7B%0A%20%20%20%20delay(100)%3B%0A%20%20%7D%0A%20%20Serial.println(%22BLE%20ready%22)%3B%0A%20%20Serial.print(%22device%20address%20is%3A%22)%3B%0A%20%20Serial.println(LBLE.getDeviceAddress())%3B%20%2F%2F%E9%A1%AF%E7%A4%BA7697%20BLE%20addr%0A%0A%20%20%2F%2F%E8%A8%AD%E5%AE%9A%E5%BB%A3%E6%92%AD%E8%B3%87%E6%96%99%0A%20%20%2F%2F%20In%20this%20case%2C%20we%20simply%20create%20an%20advertisement%20that%20represents%20an%0A%20%20%2F%2F%20connectable%20device%20with%20a%20device%20name%0A%20%20LBLEAdvertisementData%20advertisement%3B%0A%20%20advertisement.configAsConnectableDevice(%22BLE%20LED%22)%3B%0A%0A%20%20%2F%2F%20Configure%20our%20device’s%20Generic%20Access%20Profile’s%20device%20name%0A%20%20%2F%2F%20Ususally%20this%20is%20the%20same%20as%20the%20name%20in%20the%20advertisement%20data.%0A%20%20LBLEPeripheral.setName(%22BLE%20LED%22)%3B%0A%0A%20%20%2F%2F%20Add%20characteristics%20into%20ledService%0A%20%20ledService.addAttribute(switchCharacteristic)%3B%0A%0A%20%20%2F%2F%20Add%20service%20to%20GATT%20server%20(peripheral)%0A%20%20LBLEPeripheral.addService(ledService)%3B%0A%0A%20%20%2F%2F%E5%95%9F%E5%8B%95GATT%E4%BC%BA%E6%9C%8D%E5%99%A8%EF%BC%8C%E6%AD%A4%E6%99%82%E5%B7%B2%E5%8F%AF%E8%A2%AB%E9%80%A3%E7%B7%9A%0A%20%20LBLEPeripheral.begin()%3B%0A%0A%20%20%2F%2F%E9%96%8B%E5%A7%8B%E5%BB%A3%E6%92%AD%0A%20%20LBLEPeripheral.advertise(advertisement)%3B%0A%7D%0A%0Avoid%20loop()%20%7B%0A%20%20delay(100)%3B%0A%20%20if%20(switchCharacteristic.isWritten())%20%7B%0A%20%20%20%20const%20char%20value%20%3D%20switchCharacteristic.getValue()%3B%0A%20%20%20%20switch%20(value)%20%7B%0A%20%20%20%20%20%20case%20’1’%3A%0A%20%20%20%20%20%20%20%20digitalWrite(LED_BUILTIN%2C%20HIGH)%3B%20%2F%2F7697%E5%B0%B1%E6%98%AFD7%2C%20%E5%85%B6%E4%BB%96Arduino%E7%82%BAD13%0A%20%20%20%20%20%20%20%20break%3B%0A%20%20%20%20%20%20case%20’0’%3A%0A%20%20%20%20%20%20%20%20digitalWrite(LED_BUILTIN%2C%20LOW)%3B%0A%20%20%20%20%20%20%20%20break%3B%0A%20%20%20%20%20%20default%3A%0A%20%20%20%20%20%20%20%20break%3B%0A%20%20%20%20%7D%0A%20%20%7D%0A%7D%0A” message=”” highlight=”” provider=”manual”/]

Android端(App Inventor)

要寫一個小程式來與7697互動,在此推薦使用App Inventor來編寫Android端程式,一方面是圖形化介面開發非常快,另一方面則是手機端有許多豐富的資源,可以讓您的專題更完整。別忘了參考CAVEDU所開發的App Inventor中文學習網喔!

請登入App Inventor (http://ai2.appinventor.mit.edu) 之後建立一個新的專案,或由此下載本範例所需之 .aia 檔

 

Designer 頁面

本範例用到的元件有多個按鈕、顯示訊息用的 Labe; 當然也需要用到最新的 BluetoothLE 元件。Designer 頁面完成如下圖,您只要做得差不多就好,不需要一模一樣。

 

Blocks 頁面

STEP1:宣告 addr 變數,代表所要連線的7697 藍牙位址。

Screen1初始化時,要求 BluetoothLE元件開始掃瞄,連線成功的話會自動呼叫BluetoothLE.Connnected事件,並修改相關元件的設定。

STEP2:按下連線按鈕時,根據按鈕的文字來決定要連線或斷線。您也可以使用兩個獨立的按鈕來做到這件事。

STEP3  :斷線時呼叫BluetoothLE事件,並將元件回復到初始狀態等候下一次連線。

 

STEP4  :

按下按鈕時,透過 WriteIntValue 方法分別發送 1 與 0 出去,7697 接到這兩個整數值之後就會執行對應的動作。您可以依照這樣的架構加入更多按鈕或其他二元觸發條件(例如Google語音辨識、感測器值等等)。別忘了 7697 端也要加入更多case 來接收您新增的控制碼。

操作

請先上傳7697端的程式並開啟serial monitor來看看狀態,在此7697是一個peripheral。請點選App端的 Connect按鈕就會連線,連線成功即可點選ON/OFF按鈕(在連線成功之前不可按)來控制7697的 D7 USR LED燈亮滅。

 

相關文章:

9 thoughts on “如何使用LinkIt 7697開發板的BLE藍牙功能?

  1. Alvis says:

    請問這個問題是出在哪裡?要如何解決?Error 3300: Error 9007 in Extension BlutoothLE: You cannot connect to a device when the device list is empty! Try scanning again”

    • CAVEDU 阿吉 - 雜工 says:

      您好,請先確認 linkit 7697的BLE程式有正常執行。關閉手機藍牙並退出app。再次開啟app,會詢問您是否要開啟藍牙,再次開啟之後稍等應該就可以了。錯誤訊息代表未掃描到鄰近的BLE裝置,多試幾次吧。

  2. Grissom says:

    您好我想要請教一下, 我遇到在mit ai2 使用 ble 無法連接
    使用內設範例”SimplePeripheral”, 在LinkIt7697內運行正常,AI2也是使用下載範例可正常傳上Android 手機(infocus)對應的MIT AI Companion APP中,但進行Connect就出現錯誤。 請問各位大大可有解決辦法?已經搞了二天無法解決…

    錯誤訊息為: Invoke: no method named ‘ConnectWithAddress’ in class java.lang.Boolean

    Thanks a lot.

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *