WebUSB API 可將 USB 帶到網路,讓 USB 更加安全且易於使用。
如果我說簡單的「USB」,就算是 能立即構思鍵盤、滑鼠、音訊、視訊和儲存裝置。您 不過您可以找到其他種類的通用序列匯流排 (USB) 裝置 好在那裡。
這些非標準化 USB 裝置需要硬體供應商編寫平台專屬 和 SDK,讓您 (開發人員) 能夠使用。 目前,這個平台專屬程式碼一直以來都禁止使用這些裝置 都是由網路提供這也是 WebUSB API 成立的原因之一: 可讓您將 USB 裝置服務公開發布到網路。有了這個 API 製造商將能建構跨平台 JavaScript SDK 裝置。
最重要的是,這個做法讓 USB 更加安全且易於使用 到網路。
現在來看看使用 WebUSB API 可預期會出現的行為:
- 購買 USB 裝置。
- 插入電腦。右側會立即顯示通知 這部裝置上前往的頁面。
- 按一下通知。網站就在眼前,可立即使用!
- 點選即可連線,Chrome 隨即顯示 USB 裝置選擇工具,供您 挑選您的裝置。
好了!
如果沒有 WebUSB API,這項程序會是什麼樣子?
- 安裝平台專屬應用程式。
- 如果我的作業系統支援這項功能,請確認是否已下載 我們也可以把資料放在正確的地方
- 安裝軟體。幸運的話,就不會得到可怕的 OS 提示或彈出式視窗 警告您從網際網路安裝驅動程式/應用程式。如果 你幸運,安裝的驅動程式或應用程式運作不正常 您也能選擇重設 VM 這類似於按下電腦的重設按鈕(切記,網路在設計時包含故障的問題 網站)。
- 如果該項功能僅使用一次,驗證碼會保存在電腦上,直到 並考慮移除該內容(在網路上,未使用的空間最終會 reclaimed.)
開始前
本文假設您已具備一些 USB 運作方式的基本知識。如果不是,我 建議您閱讀 NutShell 中的 USB。如需 USB 的背景資訊, 請參閱官方 USB 規格。
WebUSB API 適用於 Chrome 61。
適用於來源試用
使用 WebUSB 盡可能從開發人員取得意見回饋 API 中,我們先前已在 Chrome 54 和 Chrome 中新增這項功能 57 為來源試用。
最近一次的試用期已於 2017 年 9 月順利完成。
隱私權與安全性
僅限 HTTPS
由於這項功能相當強大,因此只能在安全內容上運作。也就是說 您在建構時應考量TLS。
使用者必須做出手勢
基於安全考量,navigator.usb.requestDevice()
可能僅
可透過使用者手勢 (例如觸控或滑鼠點擊) 來呼叫。
權限政策
權限政策是一種機制,可讓開發人員選擇性地啟用 並停用各種瀏覽器功能和 API可以透過 HTTP 標頭和/或 iframe「允許」屬性。
您可以定義權限政策,控管 usb
屬性是否為
。
以下示例為禁止 WebUSB 的標頭政策:
Feature-Policy: fullscreen "*"; usb "none"; payment "self" https://payment.example.com
以下是另一個可使用 USB 的容器政策範例:
<iframe allowpaymentrequest allow="usb; fullscreen"></iframe>
開始寫程式
WebUSB API 極度依賴 JavaScript Promise。如果還不熟悉
建議您參考這個優質的 Promise 教學課程。還有一件事,() => {}
就是 ECMAScript 2015 箭頭函式。
取得 USB 裝置存取權
您可以使用以下程式碼,提示使用者選取一部已連接的 USB 裝置:
navigator.usb.requestDevice()
或呼叫 navigator.usb.getDevices()
來取得
列出網站有權存取的所有已連接 USB 裝置清單。
navigator.usb.requestDevice()
函式使用必要的 JavaScript 物件
定義 filters
。這些篩選器可將任何 USB 裝置與
提供的供應商 (vendorId
) 和產品 (productId
) ID (選用)。
classCode
、protocolCode
、serialNumber
和 subclassCode
鍵可以
也可以在這裡定義文字
舉例來說,以下說明如何存取已設定的 Arduino 裝置 來允許來源
navigator.usb.requestDevice({ filters: [{ vendorId: 0x2341 }] })
.then(device => {
console.log(device.productName); // "Arduino Micro"
console.log(device.manufacturerName); // "Arduino LLC"
})
.catch(error => { console.error(error); });
在你提問之前,我對這個 0x2341
的十六進位問題不難產生
號碼。我只是在搜尋「Arduino」這個字時參閱 USB ID 清單。
上述的執行要求中傳回的 USB device
有一些基本但
裝置的重要資訊,例如支援的 USB 版本。
封包大小上限、供應商和產品 ID、可能的
裝置可有的配置設定基本上,其中包含
裝置 USB 描述元。
// Get all connected USB devices the website has been granted access to.
navigator.usb.getDevices().then(devices => {
devices.forEach(device => {
console.log(device.productName); // "Arduino Micro"
console.log(device.manufacturerName); // "Arduino LLC"
});
})
順帶一提,如果 USB 裝置宣布 支援 WebUSB,以及 定義到達網頁網址時,Chrome 會在 已連接 USB 裝置。按一下這則通知即可開啟到達網頁。
與 Arduino USB 電路板交談
現在來看看如何透過與 WebUSB 相容,輕鬆進行通訊 Arduino 電路板會上方的 USB 連接埠。相關操作說明請參閱: https://github.com/webusb/arduino 透過 WebUSB 啟用素描。
別擔心,我們會在後續章節中說明下文提及的所有 WebUSB 裝置方法 這篇文章。
let device;
navigator.usb.requestDevice({ filters: [{ vendorId: 0x2341 }] })
.then(selectedDevice => {
device = selectedDevice;
return device.open(); // Begin a session.
})
.then(() => device.selectConfiguration(1)) // Select configuration #1 for the device.
.then(() => device.claimInterface(2)) // Request exclusive control over interface #2.
.then(() => device.controlTransferOut({
requestType: 'class',
recipient: 'interface',
request: 0x22,
value: 0x01,
index: 0x02})) // Ready to receive data
.then(() => device.transferIn(5, 64)) // Waiting for 64 bytes of data from endpoint #5.
.then(result => {
const decoder = new TextDecoder();
console.log('Received: ' + decoder.decode(result.data));
})
.catch(error => { console.error(error); });
請注意,我目前使用的 WebUSB 程式庫只是實作 一個範例通訊協定 (根據標準 USB 序列通訊協定) 以及 製造商可以建立任意類型的端點組合 控制移轉作業特別適合用於小型設定指令 因為他們會優先搭乘公車,且其結構定義明確
這是上傳至 Arduino 看板的草圖。
// Third-party WebUSB Arduino library
#include <WebUSB.h>
WebUSB WebUSBSerial(1 /* https:// */, "webusb.github.io/arduino/demos");
#define Serial WebUSBSerial
void setup() {
Serial.begin(9600);
while (!Serial) {
; // Wait for serial port to connect.
}
Serial.write("WebUSB FTW!");
Serial.flush();
}
void loop() {
// Nothing here for now.
}
上述程式碼範例中使用的第三方 WebUSB Arduino 程式庫會 基本上有兩件事
- 裝置如同 WebUSB 裝置,可讓 Chrome 讀取到達網頁網址。
- 它會公開 WebUSB Serial API,可用於覆寫預設 API。
再次查看 JavaScript 程式碼,取得使用者挑選的 device
後
device.open()
會執行所有平台專用步驟,透過 USB 啟動工作階段
裝置。接著,我需要選取可用的 USB 設定
device.selectConfiguration()
。請注意,設定會指定
裝置有電源、最大耗電量和介面數量。
說到介面,我還需要向
device.claimInterface()
,因為資料只能轉移至介面或
並宣告所有關聯端點最後通話
您必須擁有 device.controlTransferOut()
,才能根據
使用適當指令來進行 WebUSB Serial API 通訊。
接著,device.transferIn()
會執行大量轉移作業,轉移至
裝置以通知主機已準備好接收大量資料。接著,
該保證會由包含 DataView data
的 result
物件來履行
才能正確剖析
如果你熟悉 USB,所有介面應該都不陌生。
我想要更多
WebUSB API 可讓您與所有 USB 傳輸/端點類型互動:
- 控制轉乘,用於傳送或接收設定或指令
這兩個參數新增至 USB 裝置,都是透過
controlTransferIn(setup, length)
和controlTransferOut(setup, data)
處理。 - 內部傳輸用於少量具時效性的資料
使用的方法與 BULK 轉乘
《
transferIn(endpointNumber, length)
》和《transferOut(endpointNumber, data)
》。 - ISOCHRONOUS 傳輸 (用於影片和音訊等資料串流)
由
isochronousTransferIn(endpointNumber, packetLengths)
和isochronousTransferOut(endpointNumber, data, packetLengths)
。 - BULK 轉移,用於在
是可靠的方式,由
transferIn(endpointNumber, length)
處理,並transferOut(endpointNumber, data)
。
建議您也參考 Mike Tsao 的 WebLight 專案 提供了實際範例,說明如何建立採用 USB 控制的 LED 裝置 適用於 WebUSB API (這裡不是使用 Arduino)。你會找到軟硬體 和韌體。
撤銷 USB 裝置的存取權
網站可以清除網站不再需要的 USB 裝置權限
在 USBDevice
執行個體上呼叫 forget()
。舉例來說
一個在許多裝置共用電腦上使用的教育網頁應用程式
因為使用者產生的權限數量過多都會對使用者體驗造成負面影響。
// Voluntarily revoke access to this USB device.
await device.forget();
由於 forget()
適用於 Chrome 101 以上版本,因此請確認這項功能是否適用
下列項目支援:
if ("usb" in navigator && "forget" in USBDevice.prototype) {
// forget() is supported.
}
移轉作業大小限制
部分作業系統對可獲得的資料量設有限制 未完成的 USB 交易。將資料分割成小型交易 一次提交多個範本有助於避免這類限制。並降低 記憶體用量,並讓應用程式 項傳輸作業完成。
提交至端點的多次傳輸一律會按順序執行 提交多個已排入佇列的區塊,以避免 USB 傳輸之間的延遲時間每次完整傳送區塊時 通知程式碼,說明應提供更多資料,如輔助程式中所述 函式範例。
const BULK_TRANSFER_SIZE = 16 * 1024; // 16KB
const MAX_NUMBER_TRANSFERS = 3;
async function sendRawPayload(device, endpointNumber, data) {
let i = 0;
let pendingTransfers = [];
let remainingBytes = data.byteLength;
while (remainingBytes > 0) {
const chunk = data.subarray(
i * BULK_TRANSFER_SIZE,
(i + 1) * BULK_TRANSFER_SIZE
);
// If we've reached max number of transfers, let's wait.
if (pendingTransfers.length == MAX_NUMBER_TRANSFERS) {
await pendingTransfers.shift();
}
// Submit transfers that will be executed in order.
pendingTransfers.push(device.transferOut(endpointNumber, chunk));
remainingBytes -= chunk.byteLength;
i++;
}
// And wait for last remaining transfers to complete.
await Promise.all(pendingTransfers);
}
提示
使用內部頁面 about://device-log
,在 Chrome 中對 USB 偵錯變得更容易
集中查看所有 USB 裝置相關事件。
你也可以參考 about://usb-internals
內部網頁
模擬虛擬 WebUSB 裝置的連線和中斷連線。
這對於在無需實際硬體的情況下執行 UI 測試非常實用。
在大部分的 Linux 系統中,USB 裝置會對應唯讀權限,
預設值。如要允許 Chrome 開啟 USB 裝置,您必須新增 udev
規則。在 /etc/udev/rules.d/50-yourdevicename.rules
中建立檔案,並使用
以下內容:
SUBSYSTEM=="usb", ATTR{idVendor}=="[yourdevicevendor]", MODE="0664", GROUP="plugdev"
舉例來說,如果您的裝置是 Arduino,則 [yourdevicevendor]
為 2341
。
您也可以為更具體的規則新增 ATTR{idProduct}
。請確認
user
是 plugdev
群組的成員。接下來只要重新連接裝置即可。
資源
- Stack Overflow:https://stackoverflow.com/questions/tagged/webusb
- WebUSB API 規格:http://wicg.github.io/webusb/
- Chrome 功能狀態:https://www.chromestatus.com/feature/5651917954875392
- 規格問題:https://github.com/WICG/webusb/issues
- 實作錯誤:http://crbug.com?q=component:Blink>USB
- WebUSB ❤ ️Arduino:https://github.com/webusb/arduino
- IRC:#webusb (W3C 的 IRC)
- WICG 郵寄清單:https://lists.w3.org/Archives/Public/public-wicg/
- WebLight 專案:https://github.com/sowbug/weblight
使用主題標記將推文傳送至 @ChromiumDev
#WebUSB
敬上
,並說明你使用這項服務的位置和方式。
特別銘謝
感謝 Joe Medley 撰寫這篇文章。