藍牙

本文件說明如何使用藍牙藍牙通訊端藍牙偏低 Energy API 用於與藍牙和藍牙低功耗裝置通訊。

如需藍牙的背景資訊,請參閱官方藍牙規格

資訊清單規定

針對使用藍牙的 Chrome 應用程式,請在資訊清單中新增 bluetooth 項目,並指定是否 以及您要實作的設定檔、通訊協定或服務的 UUID,以及 您想使用通訊端和/或低功耗 API 實作這些項目

例如使用通訊端實作:

"bluetooth": {
  "uuids": [ "1105", "1106" ],
  "socket": true
}

針對低功耗實施:

"bluetooth": {
  "uuids": [ "180D", "1809", "180F" ],
  "low_energy": true
}

如果只要存取轉接器狀態、探索鄰近裝置,以及取得裝置的基本資訊, 只需要項目本身:

"bluetooth": {}

轉接器資訊

取得轉接程式狀態

如要取得藍牙轉接器的狀態,請使用 bluetooth.getAdapterState 方法:

chrome.bluetooth.getAdapterState(function(adapter) {
  console.log("Adapter " + adapter.address + ": " + adapter.name);
});

轉接器通知

每當轉接程式狀態變更,系統就會傳送 bluetooth.onAdapterStateChanged 事件。這可以 ,例如判斷轉接器無線電何時開啟或關閉。

var powered = false;
chrome.bluetooth.getAdapterState(function(adapter) {
  powered = adapter.powered;
});

chrome.bluetooth.onAdapterStateChanged.addListener(
  function(adapter) {
    if (adapter.powered != powered) {
      powered = adapter.powered;
      if (powered) {
        console.log("Adapter radio is on");
      } else {
        console.log("Adapter radio is off");
      }
    }
  });

裝置資訊

列出已知裝置

如要查看藍牙轉接器已知的裝置清單,請使用 bluetooth.getDevices 方法:

chrome.bluetooth.getDevices(function(devices) {
  for (var i = 0; i < devices.length; i++) {
    console.log(devices[i].address);
  }
});

系統會退還所有裝置,包括最近找到的配對裝置和裝置。這不會 開始探索新裝置 (請參閱「探索附近的裝置」)。

接收裝置通知

您不必重複呼叫 bluetooth.getDevicesbluetooth.onDeviceAddedbluetooth.onDeviceChangedbluetooth.onDeviceRemoved 活動以接收通知。

每當轉接器偵測到裝置時,系統就會傳送 bluetooth.onDeviceAdded 事件; 連線至轉接器:

chrome.bluetooth.onDeviceAdded.addListener(function(device) {
  console.log(device.address);
});

新增這個事件的事件監聽器後,系統並不會開始探索裝置 (請參閱「探索附近裝置 裝置)。

裝置變更 (包括先前發現的裝置正在配對) 會由 bluetooth.onDeviceChanged 事件:

chrome.bluetooth.onDeviceChanged.addListener(function(device) {
  console.log(device.address);
});

最後,每當配對的裝置移除配對裝置時,系統都會傳送 bluetooth.onDeviceRemoved 事件 或最近未偵測到的裝置:

chrome.bluetooth.onDeviceRemoved.addListener(function(device) {
  console.log(device.address);
});

探索附近的裝置

如要開始探索鄰近裝置,請使用 bluetooth.startDiscovery 方法。探索可以 需要耗用大量資源,因此建議您在完成後呼叫 bluetooth.stopDiscovery

每當應用程式需要探索鄰近裝置時,建議您呼叫 bluetooth.startDiscovery。 請勿對 bluetooth.AdapterStatediscovering 屬性發出呼叫條件。 因此即使其他應用程式偵測到鄰近裝置,通話仍會順利進行,且可確保轉接器 會在其他應用程式停止後繼續探索。

使用 bluetooth.onDeviceAdded 接收每部新發現裝置的相關資訊 活動。僅限近期發現或先前已配對的裝置 就不會傳送活動請改為呼叫 bluetooth.getDevices, 取得最新資訊,並使用 bluetooth.onDeviceChanged 事件接收 研究結果是否出了任何變動

範例:

var device_names = {};
var updateDeviceName = function(device) {
  device_names[device.address] = device.name;
};
var removeDeviceName = function(device) {
  delete device_names[device.address];
}

// Add listeners to receive newly found devices and updates
// to the previously known devices.
chrome.bluetooth.onDeviceAdded.addListener(updateDeviceName);
chrome.bluetooth.onDeviceChanged.addListener(updateDeviceName);
chrome.bluetooth.onDeviceRemoved.addListener(removeDeviceName);

// With the listeners in place, get the list of devices found in
// previous discovery sessions, or any currently active ones,
// along with paired devices.
chrome.bluetooth.getDevices(function(devices) {
  for (var i = 0; i < devices.length; i++) {
    updateDeviceName(devices[i]);
  }
});

// Now begin the discovery process.
chrome.bluetooth.startDiscovery(function() {
  // Stop discovery after 30 seconds.
  setTimeout(function() {
    chrome.bluetooth.stopDiscovery(function() {});
  }, 30000);
});

如果使用者關閉藍牙無線電,所有探索工作階段都會結束,不會繼續 並自動同步。如果這對您的應用程式而言很重要,您應該觀看 bluetooth.onAdapterStateChanged 事件。如果 discovering 屬性變更為 false,則 您的應用程式必須再次呼叫 bluetooth.startDiscovery 才能恢復運作。請注意 要投入大量資源來探索內容。

辨識不同裝置

我們提供多種選項,可用來識別由 bluetooth.getDevices 和相關事件,

如果裝置支援藍牙裝置 ID 規格,即可新增多項屬性 Device 物件,其中包含該規格定義的欄位。範例:

chrome.bluetooth.getDevices(function(devices) {
  for (var i = 0; i < devices.length; i++) {
    if (devices[0].vendorIdSource != undefined) {
      console.log(devices[0].address + ' = ' +
                  devices[0].vendorIdSource + ':' +
                  devices[0].vendorId.toString(16) + ':' +
                  devices[0].productId.toString(16) + ':' +
                  devices[0].deviceId.toString(16));
    }
  }
});

使用裝置 ID 規格通常足以識別特定型號,甚至是修訂版本 以及供應商提供的裝置權限如果不是,就必須依賴 裝置的類別或類型,是否要與 address 中的製造商前置字串搭配使用。

大多數藍牙裝置提供「裝置資訊」類別,做為根據 基頻已指派編號文件。您可以在 deviceClass 中使用這個位元欄位 資源。

chrome.bluetooth.getDevices(function(devices) {
  for (var i = 0; i < devices.length; i++) {
    if (devices[0].vendorIdSource != undefined) {
      console.log(devices[0].address + ' = ' +
                  devices[0].deviceClass.toString(16));
    }
  }
});

剖析欄位可能相當複雜,因此,Chrome 會替最常見的裝置類型處理這項作業, 會設定 type 欄位。如果無法提供這個建議地區,或是因您的需求不敷所需,建議您 自行剖析 deviceClass

chrome.bluetooth.getDevices(function(devices) {
  for (var i = 0; i < devices.length; i++) {
    if (devices[0].vendorIdSource != undefined) {
      console.log(devices[0].address + ' = ' + devices[0].type);
    }
  }
});

使用 RFCOMM 和 L2CAP

Chrome 應用程式可與任何支援 RFCOMM 或 L2CAP 服務的裝置建立連線。這包括 市面上大多數常見的藍牙裝置

連線至通訊端

如果要連結裝置,您需要有三項設備。用來建立連線的通訊端 是使用 bluetoothSocket.create 建立而成要連線的裝置位址 以及服務本身的 UUID

建立連線前,您應使用 bluetooth.getDevice 或裝置探索 API。

建立基礎連線所需的資訊,包括 應使用 L2CAP 通訊協定,以及透過伺服器上的 SDP 探索取得哪個通道或 PSM 裝置。

範例:

var uuid = '1105';
var onConnectedCallback = function() {
  if (chrome.runtime.lastError) {
    console.log("Connection failed: " + chrome.runtime.lastError.message);
  } else {
    // Profile implementation here.
  }
};

chrome.bluetoothSocket.create(function(createInfo) {
  chrome.bluetoothSocket.connect(createInfo.socketId,
    device.address, uuid, onConnectedCallback);
});

保留 socketId,以便稍後傳送資料 (bluetoothSocket.send) 通訊端。

從通訊端接收並傳送至通訊端

接收來自通訊端的資料,以及傳送至通訊端的資料會使用 ArrayBuffer 物件。如要瞭解 ArrayBuffers 請參閱簡介、JavaScript 類型陣列,以及「如何轉換 ArrayBuffer 」教學課程

如要傳送 arrayBuffer 中的資料,請使用 bluetoothSocket.send

chrome.bluetoothSocket.send(socketId, arrayBuffer, function(bytes_sent) {
  if (chrome.runtime.lastError) {
    console.log("Send failed: " + chrome.runtime.lastError.message);
  } else {
    console.log("Sent " + bytes_sent + " bytes")
  }
})

相比傳送資料的方法,則是在事件中接收資料 (bluetoothSocket.onReceive.通訊端建立作業已取消暫停 (請參閱 bluetoothSocket.setPaused) 因此,這個事件的事件監聽器通常會在 bluetoothSocket.createbluetoothSocket.connect

chrome.bluetoothSocket.onRecieve.addListener(function(receiveInfo) {
  if (receiveInfo.socketId != socketId)
    return;
  // receiveInfo.data is an ArrayBuffer.
});

接收通訊端錯誤和連線中斷

如要收到通訊端錯誤 (包括中斷連線) 的通知,請將事件監聽器新增至 bluetoothSocket.onReceiveError 事件。

chrome.bluetoothSocket.onReceiveError.addListener(function(errorInfo) {
  // Cause is in errorInfo.error.
  console.log(errorInfo.errorMessage);
});

中斷與通訊端的連線

如要掛斷連線,並中斷通訊端的連線,請使用 bluetoothSocket.disconnect

chrome.bluetoothSocket.disconnect(socketId);

出版服務

除了為裝置建立對外連線外,Chrome 應用程式也會發布 或用於其他支援 RFCOMM 或 L2CAP 的裝置。

透過通訊端聆聽

系統支援兩種類型的已發布服務。RFCOMM 是最常用到的 大多數的裝置和設定檔:

var uuid = '1105';
chrome.bluetoothSocket.create(function(createInfo) {
  chrome.bluetoothSocket.listenUsingRfcomm(createInfo.socketId,
    uuid, onListenCallback);
});

L2CAP 是其他裝置類型和供應商專屬用途,例如韌體 上傳。

var uuid = '0b87367c-f188-47cd-bc20-a5f4f70973c6';
chrome.bluetoothSocket.create(function(createInfo) {
  chrome.bluetoothSocket.listenUsingL2cap(createInfo.socketId,
    uuid, onListenCallback);
});

在這兩種情況下,您都可以傳送選用的 bluetoothSocket.ListenOptions 來分配 管道或 PSM回呼代表透過 chrome.runtime.lastError 發生錯誤,成功 反之。保留 socketId 的控制代碼,以便稍後接受連線 (bluetoothSocket.onAccept)。

接受用戶端連線

用戶端連線會由 bluetoothSocket.onAccept 事件。

chrome.bluetoothSocket.onAccept.addListener(function(acceptInfo) {
  if (info.socketId != serverSocketId)
    return;

  // Say hello...
  chrome.bluetoothSocket.send(acceptInfo.clientSocketId,
    data, onSendCallback);

  // Accepted sockets are initially paused,
  // set the onReceive listener first.
  chrome.bluetoothSocket.onReceive.addListener(onReceive);
  chrome.bluetoothSocket.setPaused(false);
});

停止接受用戶端連線

如要停止接受用戶端連線並取消發布服務,請使用 bluetoothSocket.disconnect

chrome.bluetoothSocket.disconnect(serverSocketId);

與低功耗裝置互動

藍牙低功耗 (Bluetooth Smart) 和採用低功耗的無線技術 提高用量上限Bluetooth Low Energy API 可讓應用程式根據 和周邊裝置的 LE 連線。以下章節將說明如何探索、連結及 與藍牙低功耗週邊裝置互動。

探索週邊裝置並連線至週邊裝置

與傳統藍牙裝置一樣,可透過以下方法尋找 LE 週邊裝置 「探索鄰近裝置」一節。LE 裝置會傳送資料封包,使裝置進入偵測狀態 稱為「廣告資料」且裝置處於廣告模式狀態。廣告資料 可能包含裝置可用的服務 UUID。如有的話,這些 UUID 可使用對應 bluetooth.Device 物件的 uuids 屬性存取。

發現後,只要呼叫 bluetoothLowEnergy.connect 即可連線到 LE 裝置。 應用程式可與服務互動:

chrome.bluetooth.onDeviceAdded.addListener(function(device) {
  var uuid = '0000180d-0000-1000-8000-00805f9b34fb';
  if (!device.uuids || device.uuids.indexOf(uuid) < 0)
    return;

  // The device has a service with the desired UUID.
  chrome.bluetoothLowEnergy.connect(device.address, function () {
    if (chrome.runtime.lastError) {
      console.log('Failed to connect: ' + chrome.runtime.lastError.message);
      return;
    }

    // Connected! Do stuff...
    ...
  });
});

連線後,對應 bluetooth.Device 物件的 connected 屬性就會 值為 true。呼叫 bluetoothLowEnergy.connect 即可由 應用程式的實際連線裝置可能存在實體連線 完全不必呼叫 bluetoothLowEnergy.connect 即可 (例如因為其他應用程式)。於 在此情況下,您的應用程式仍可與裝置的服務互動, 一律呼叫 bluetoothLowEnergy.connect,以防止其他應用程式中斷 實體連結。

當應用程式不再需要連線時,可於以下時間移除其連線宣告: 呼叫 bluetoothLowEnergy.disconnect

chrome.bluetoothLowEnergy.disconnect(deviceAddress);

請注意,這麼做並不會刪除裝置的實體連結,因為可能會有其他 具備有效連線的應用程式。有時裝置可能會 連線中斷,原因是無法由應用程式控制 (例如裝置 使用者因作業系統的公用程式而消失或直接中斷連線)。 應用程式應觀察 bluetooth.onDeviceChanged 事件,藉此接收變更的通知 並視情況重新連線

連線後,執行 Chrome 的裝置將成為主要角色, 遠端裝置應具備週邊裝置角色。此時,應用程式 與裝置上的服務互動。注意: API 目前不支援做為 LE 週邊裝置,應用程式只能實作中樞角色。

服務、特性和描述元

藍牙低功耗技術是以簡單的要求/回應通訊協定為基礎,這個通訊協定稱為屬性通訊協定 (ATT)。使用 ATT,讓中央裝置與週邊裝置上的稱為屬性互動 方法是連上名為「一般屬性設定檔」 (GATT) 的特殊藍牙設定檔。GATT 定義下列高階概念:

  • 服務:GATT 服務代表一系列資料及相關行為,目的在於達成 裝置的特定功能例如,心率監測器通常至少需要有 一項「心率服務」GATT 服務相關資訊包含在 bluetoothLowEnergy.Service 物件。
  • 特性:GATT 特性是一種用於建構 GATT 服務的基本資料元素 ,其包含值以及定義該值存取方式的屬性。例如: 「心率服務」具有「心率測量」也就是用來瞭解 使用者心率的值。GATT 特性的相關資訊包含在 bluetoothLowEnergy.Characteristic 物件。
  • 描述元:GATT 特性描述元包含關於特性的進一步資訊。 GATT 特性描述元的相關資訊包含在 bluetoothLowEnergy.Descriptor 物件。

Bluetooth Low Energy API 可讓應用程式找出裝置的 呼叫 bluetoothLowEnergy.getServices 並呼叫 bluetoothLowEnergy.getCharacteristicsbluetoothLowEnergy.getDescriptors。應用程式可以 比較服務的 uuid 欄位與 所需的 GATT UUID:

chrome.bluetoothLowEnergy.getServices(deviceAddress, function(services) {
  ...
  for (var i = 0; i < services.length; i++) {
    if (services[i].uuid == HEART_RATE_SERVICE_UUID) {
      heartRateService = services[i];
      break;
    }
  }
  ...
});

凡是可透過 API 存取的服務、特性和描述元都會獲得指派的專屬 執行個體 ID,您可以使用 instanceId 欄位取得。您可以使用 用於識別 GATT 物件並執行特定操作:

chrome.bluetoothLowEnergy.getCharacteristics(heartRateService.instanceId,
                                             function(chracteristics) {
  ...
  for (var i = 0; i < characteristics.length; i++) {
    if (characteristics[i].uuid == HEART_RATE_MEASUREMENT_UUID) {
      measurementChar = characteristics[i];
      break;
    }
  }
  ...
  chrome.bluetoothLowEnergy.getDescriptors(measurementChar.instanceId,
                                           function(descriptors) {
    ...
  });
});

服務事件

裝置連線後,Chrome 就會探索其中的服務。使用者找到各項服務後 移除後,該應用程式會收到 bluetoothLowEnergy.onServiceAddedbluetoothLowEnergy.onServiceRemoved 事件:

  var initializeService = function(service) {
    if (!service) {
      console.log('No service selected!');
      // Reset UI, etc.
      ...
      return;
    }

    myService = service;

    // Get all the characteristics and descriptors and bootstrap the app.
    ...
  };

  chrome.bluetoothLowEnergy.onServiceAdded.addListener(function(service) {
    if (service.uuid == MY_SERVICE_UUID)
      initializeService(service);
  });

  chrome.bluetoothLowEnergy.onServiceRemoved.addListener(function(service) {
    if (service.instanceId == myService.instanceId)
      initializeService(null);
  });

Chrome 會以非同步的方式探索服務的所有特性和描述元,然後將 探索完成後,即會執行 bluetoothLowEnergy.onServiceAdded 事件。如果連線至 當週邊裝置終止時,Chrome 會移除所有相關服務,並傳送 bluetoothLowEnergy.onServiceRemoved 事件。

某些週邊裝置可能會修改其服務,例如:服務特性可能有所變更或 可能會完全新增或移除服務Chrome 會使用 bluetoothLowEnergy.onServiceChangedbluetoothLowEnergy.onServiceAddedbluetoothLowEnergy.onServiceRemoved 事件。

  chrome.bluetoothLowEnergy.onServiceChanged.addListener(function(service) {
    if (service.instanceId != myService.instanceId)
      return;

    updateMyService(service);
  });

讀取及寫入特徵值

GATT 特性會對服務的一個方面進行編碼。中央應用程式讀取、執行與修改 週邊裝置服務的狀態。特性 值是位元組序列,其意義是由定義的高階規格所定義 特定特性例如「心率測量」特性的值 將使用者的心率和卡路里燃燒總量編碼。人體感測器 位置特性會對心率感測器在人體中的適當位置進行編碼,

Chrome 提供 bluetoothLowEnergy.readCharacteristicValue 方法,以便讀取 特性:

chrome.bluetoothLowEnergy.readCharacteristicValue(chrc.instanceId,
                                                  function(result) {
  if (chrome.runtime.lastError) {
    console.log('Failed to read value: ' + chrome.runtime.lastError.message);
    return;
  }

  var bytes = new Uint8Array(result.value);

  // Do stuff with the bytes.
  ...
});

有些特徵可寫入,尤其是行為為「控制點」的特徵 每個值都有副作用舉例來說,「心率控制點」特性的用途如下: 指示心率感應器重設其燃燒的總卡路里數,但僅支援寫入。目的地: 為此,Chrome 提供 bluetoothLowEnergy.writeCharacteristicValue 方法:

var myBytes = new Uint8Array([ ... ]);
chrome.bluetoothLowEnergy.writeCharacteristicValue(chrc.instanceId,
                                                   myBytes.buffer,
                                                   function() {
  if (chrome.runtime.lastError) {
    console.log('Failed to write value: ' +
                chrome.runtime.lastError.message);
    return;
  }

  // Value is written now.
});

特性描述元的運作方式相同,且可讀取和/或可寫入。Chrome 提供 bluetoothLowEnergy.readDescriptorValuebluetoothLowEnergy.writeDescriptorValue 讀取及寫入描述元值的方法。

如要檢查某個特性是否支援讀取或寫入,應用程式可檢查 properties bluetoothLowEnergy.Characteristic 物件的欄位。雖然這個欄位 用於存取值的安全性需求資訊,則會說明哪個值 來支援一般操作機制

處理價值通知

某些特性讓其價值透過通知或指標獲得識別。舉例來說 心率測量特性無法讀取或寫入,但會在其上傳送更新 依固定間隔計算目前的值應用程式可以使用 bluetoothLowEnergy.onCharacteristicValueChanged 事件。

  chrome.bluetoothLowEnergy.onCharacteristicValueChanged.addListener(
      function(chrc) {
    if (chrc.instanceId != myCharId)
      return;

    var bytes = new Uint8Array(chrc.value);

    // Do stuff with the bytes.
    ...
  });

即使某項特性支援通知/指示,系統預設也不會啟用這些功能。一個 應用程式應呼叫 bluetoothLowEnergy.startCharacteristicNotifications 物件, bluetoothLowEnergy.stopCharacteristicNotifications 方法,用於開始或停止接收 bluetoothLowEnergy.onCharacteristicValueChanged 事件。

  // Start receiving characteristic value notifications.
  var notifying = false;
  chrome.bluetoothLowEnergy.startCharacteristicNotifications(chrc.instanceId,
                                                             function() {
    if (chrome.runtime.lastError) {
      console.log('Failed to enable notifications: ' +
                  chrome.runtime.lastError.message);
      return;
    }

    notifying = true;
  });

  ...

  // No longer interested in notifications from this characteristic.
  if (notifying) {
    chrome.bluetoothLowEnergy.stopCharacteristicNotifications(
        chrc.instanceId);
  }

通知啟動後,應用程式會收到 每當通知或指標出現 bluetoothLowEnergy.onCharacteristicValueChanged 時 從特徵中得到的回應如果這個特性支援讀取,那麼這個事件也會 成功呼叫 bluetoothLowEnergy.readCharacteristicValue 之後)。這麼做可讓應用程式 來統合透過讀取要求和通知觸發的值更新控制流程:

  chrome.bluetoothLowEnergy.onCharacteristicValueChanged.addListener(
      function(chrc) {
    // Process the value.
    ...
  });

  chrome.bluetoothLowEnergy.startCharacteristicNotifications(chrc.instanceId,
                                                             function() {
    // Notifications started. Read the initial value.
    chrome.bluetoothLowEnergy.readCharacteristicValue(chrc.instanceId,
                                                      function(result) {
      ...
      // No need to do anything here since onCharacteristicValueChanged
      // will handle it.
    });
  });

如果特性支援通知,其 properties 欄位就會包含 "notify""indicate" 屬性。

注意:如果某個特性支援通知/指標,這個欄位就會顯示「Client 特性設定來啟用/停用通知。Chrome 不允許 以便寫入這個描述元您應改用 bluetoothLowEnergy.startCharacteristicNotificationsbluetoothLowEnergy.stopCharacteristicNotifications 方法,以控制通知行為。