Dives into Chromium source code
If I said plain and simple "USB", there is a good chance that you will immediately think of keyboards, mice, audio, video and storage devices. You're right but you'll find other kinds of Universal Serial Bus (USB) devices out there.python
若是我說簡單的「USB」,頗有可能會當即想到鍵盤,鼠標,音頻,視頻和存儲設備。你是對的,但你會發現其餘種類的通用串行總線(USB)設備。linux
These non-standardized USB devices require hardware vendors to write native drivers and SDKs in order for you (the developer) to take advantage of them. Sadly this native code has historically prevented these devices from being used by the Web. And that's one of the reasons the WebUSB API has been created: to provide a way to expose USB device services to the Web. With this API, hardware manufacturers will be able to build cross-platform JavaScript SDKs for their devices. But most importantly this will make USB safer and easier to use by bringing it to the Web.git
這些非標準的USB設備要求硬件供應商編寫本機驅動程序和SDK,以便您(開發人員)利用它們。遺憾的是,本機代碼從來阻止了這些設備被Web使用。這就是WebUSB API建立的緣由之一:提供一種將USB設備服務公開到Web的方法。藉助這種API,硬件製造商將可以爲其設備構建跨平臺的JavaScript SDK。但最重要的是,這 將使USB更安全,更容易使用,將其帶到Web上。github
Let's see what you could expect with the WebUSB API:web
- Buy a USB device.
- Plug it into your computer.
- A notification appears right away, with the right website to go to for this device.
- Simply click on it. Website is there and ready to use!
- Click to connect and a USB device chooser shows up in Chrome, where you can pick your device.
- Tada!
讓咱們看看您能夠用WebUSB API預期什麼:chrome
購買USB設備。shell
將其插入計算機。express
當即出現通知,正確的網站將轉到此設備。promise
只需點擊它。網站在那裏,準備使用!
點擊鏈接,並在Chrome中顯示一個USB設備選擇器,您能夠在其中選擇您的設備。
而後!
What would this procedure be like without the WebUSB API?
- Read a box, label, or search on line and possibly end up on the wrong website.
- Have to install a native application.
- Is it supported on my operating system? Make sure you download the "right" thing.
- Scary OS prompts popup and warn you about installing drivers/applications from the Internet.
- Malfunctioning code harms the whole computer. The Web is built to contain malfunctioning websites.
- Only use the USB device once? On the Web, the website is gone once you closed tab. On a computer the code sticks around.
沒有WebUSB API,這個程序是什麼樣的?
- 閱讀框,標籤或在線搜索,可能最終在錯誤的網站上。
- 必須安裝本機應用程序。
- 是否支持個人操做系統?確保你下載了「正確」的東西。
- 可怕的操做系統提示彈出窗口並警告您有關從Internet安裝驅動程序/應用程序。
- 故障代碼損害整個計算機。網頁是爲了 包含故障的網站。
- 只能使用USB設備一次?在網絡上,一旦您關閉標籤,該網站就會消失。在電腦上,代碼貼在一塊兒。
Before we start 在咱們開始以前
This article assumes you have some basic knowledge of how USB works. If not, I recommend reading USB in a NutShell. For background information about USB, check out the official USB specifications.
The WebUSB API is available in Chrome 61.
本文假設您對USB的工做原理有一些基礎知識。若是沒有,我建議在NutShell中閱讀 USB。有關USB的背景信息,請查看 官方USB規格。
該 WebUSB API 在Chrome 61是可用的。
Available for Origin Trials 可用於原始試驗
In order to get as much feedback as possible from developers using the WebUSB API in the field, we've previously added this feature in Chrome 54 and Chrome 57 as an origin trial.
The latest trial has successfully ended in September 2017.
Privacy and security 隱私和安全
HTTPS only 僅限HTTPS
Because this API is a powerful new feature added to the Web, Chrome aims to make it available only to secure contexts. This means you'll need to build with TLS in mind.
因爲這個API是一個功能強大的新增功能,所以Chrome旨在使其可用於 安全上下文。這意味着您須要使用TLS來構建。
Note: We care deeply about security, so you will notice that new Web capabilities require HTTPS. The WebUSB API is no different, and is yet another good reason to get HTTPS up and running on your site.
做爲安全功能,navigator.usb.requestDevice
必須經過用戶手勢(如觸摸或鼠標點擊)來訪問鏈接的USB設備。
注意: 用戶手勢不會經過異步事件(如Promises)傳播。見crbug.com/404161
Feature Policy 功能政策
A feature policy is a mechanism that allows developers to selectively enable and disable various browser features and APIs. It can be defined via a HTTP header and/or an iframe "allow" attribute.
You can define a feature that controls whether the usb attribute is exposed on the Navigator object, or in other words if you allow WebUSB.
Below is an example of a header policy where WebUSB is not allowed:
功能策略是容許開發人員有選擇地啓用和禁用各類瀏覽器功能和API的機制。它能夠經過HTTP頭和/或iframe「allow」屬性進行定義。
您能夠定義一個功能,用於控制是否在Navigator對象上顯示usb屬性,或者換句話說,若是容許WebUSB。
如下是不容許使用WebUSB的標題策略的示例:
Feature-Policy: fullscreen "*"; usb "none"; payment "self" https://payment.example.com
Below is another example of a different container policy where USB is allowed:
如下是容許USB容許的其餘容器策略的另外一個示例:
<iframe allowpaymentrequest allow=’usb fullscreen’></iframe>
Let's start coding
The WebUSB API relies heavily on JavaScript Promises. If you're not familiar with them, check out this great Promises tutorial. One more thing, () => {}
are simply ECMAScript 2015 Arrow functions -- they have a shorter syntax compared to function expressions and lexically bind the value of this
.
WebUSB API很是依賴JavaScript 承諾。若是你不熟悉他們,看看這個偉大的 Promises教程。還有一件事, () => {}
就是簡單的ECMAScript 2015 Arrow函數 - 與函數表達式相比,它們具備較短的語法,而且能夠對其值進行詞法綁定 this
。
Get access to USB devices 訪問USB設備
You can either prompt the user to select a single connected USB device usingnavigator.usb.requestDevice
or call navigator.usb.getDevices
to get a list of all connected USB devices the origin has access to.
您能夠提示用戶選擇一個鏈接的USB設備,navigator.usb.requestDevice
或使用或調用 navigator.usb.getDevices
以獲取原始設備能夠訪問的全部鏈接的USB設備的列表。
The navigator.usb.requestDevice
function takes a mandatory JavaScript object that defines filters
. These filters are used to match any USB device with the given vendor (vendorId
) and optionally product (productId
) identifiers. The classCode
, protocolCode
, serialNumber
, and subclassCode
keys can also be defined there as well.
該 navigator.usb.requestDevice
函數須要一個定義的強制性JavaScript對象 filters
。這些過濾器用於將任何USB設備與給定的供應商(vendorId
)和可選的product(productId
)標識符進行匹配。在 classCode
, protocolCode
, serialNumber
,和 subclassCode
鍵也能夠在那裏定義爲好。
For instance, here's how to get access to a connected Arduino device configured to allow the origin.
例如,如下是如何訪問配置爲容許源的鏈接的Arduino設備。
navigator.usb.requestDevice({ filters: [{ vendorId: 0x2341 }] })
.then(device => {
console.log(device.productName); // "Arduino Micro"
console.log(device.manufacturerName); // "Arduino LLC"
})
.catch(error => { console.log(error); });
Before you ask, I didn't magically come up with this 0x2341
hexadecimal number. I simply searched for the word "Arduino" in this List of USB ID's.
在你問以前,我沒有神奇地想出這個 0x2341
十六進制數字。我只是在這個USB ID列表中搜索「Arduino」這個詞 。
The USB device
returned in the fulfilled promise above has some basic, yet important information about the device such as the supported USB version, maximum packet size, vendor and product IDs, the number of possible configurations the device can have - basically all fields contained in the device USB Descriptor
device
在上述履行的承諾中返回的USB 有一些關於設備的基本但重要的信息,例如支持的USB版本,最大包大小,供應商和產品ID,設備可能具備的可能配置的數量 - 基本上包含在 設備USB描述符
For info, if a USB device announces its support for WebUSB, as well as defining a landing page URL, Chrome will show a persistent notification when the USB device is plugged in. Clicking on this notification will open the landing page.
有關信息,若是USB設備宣佈 對WebUSB的支持以及定義着陸頁網址,Chrome將在USB設備插入時顯示持久的通知。點擊此通知將打開加載頁。
From there, you can simply call navigator.usb.getDevices
and get access to your Arduino device as shown below.
從那裏,您能夠直接調用
navigator.usb.getDevices
並訪問您的Arduino設備,以下所示。
navigator.usb.getDevices().then(devices => {
devices.map(device => {
console.log(device.productName); // "Arduino Micro"
console.log(device.manufacturerName); // "Arduino LLC"
});
})
Talk to an Arduino USB board
Okay, now let's see how easy it is to communicate from a WebUSB compatible Arduino board over the USB port. Check out instructions at https://github.com/webusb/arduino to WebUSB-enable your sketches.
好的,如今讓咱們看看經過USB端口與WebUSB兼容的Arduino板進行通訊是多麼容易。請訪問 https://github.com/webusb/arduino ,查看WebUSB啓用您的 草圖的說明。
Don't worry, I'll cover all the WebUSB device methods mentioned below later in this article.
不用擔憂,本文稍後將介紹全部WebUSB設備方法。
var 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 => {
let decoder = new TextDecoder();
console.log('Received: ' + decoder.decode(result.data));
})
.catch(error => { console.log(error); });
Please keep in mind that the WebUSB library we are using here is just implementing one example protocol (based on the standard USB serial protocol) and that manufacturers can create any set and types of endpoints they wish. Control transfers are especially nice for small configuration commands as they get bus priority and have a well defined structure.
And here's the sketch that has been uploaded to the Arduino board.
// Third-party WebUSB Arduino library
#include <WebUSB.h>
const WebUSBURL URLS[] = {
{ 1, "webusb.github.io/arduino/demos/" },
{ 0, "localhost:8000" },
};
const uint8_t ALLOWED_ORIGINS[] = { 1, 2 };
WebUSB WebUSBSerial(URLS, 2, 1, ALLOWED_ORIGINS, 2);
#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.
}
The third-party WebUSB Arduino library used in the sample code above does basically two things:
- The device acts as a WebUSB device enabling Chrome to read the landing page URL.
- It exposes a WebUSB Serial API that you may use to override the default one.
上面的示例代碼中使用的第三方 WebUSB Arduino庫基本上有兩件事情:
- 該設備充當WebUSB設備,使Chrome能夠讀取 加載頁網址。
- 它暴露了一個WebUSB Serial API,您可使用它來覆蓋默認的。
Let's look at the JavaScript code again. Once we get the device
picked by the user, device.open
simply runs all platform-specific steps to start a session with the USB device. Then, we have to select an available USB Configuration with device.selectConfiguration
. Remember that a Configuration specifies how the device is powered, its maximum power consumption and its number of interfaces. Talking about interfaces, we also need to request exclusive access with device.claimInterface
since data can only be transferred to an interface or associated endpoints when the interface is claimed. Finally callingdevice.controlTransferOut
is needed to set up the Arduino device with the appropriate commands to communicate through the WebUSB Serial API.
From there, device.transferIn
performs a bulk transfer onto the device to inform it that the host is ready to receive bulk data. Then, the promise is fulfilled with a result
object containing a DataView data
that has to be parsed appropriately.
For those who are familiar with USB, all of this should look pretty familiar.
咱們再來看一下JavaScript代碼。一旦咱們獲得 device
用戶的選擇, device.open
只需運行全部特定於平臺的步驟來開始與USB設備的會話。而後,咱們必須選擇一個可用的USB配置 device.selectConfiguration
。請記住,配置指定設備的供電方式,最大功耗以及接口數量。談到接口,咱們還須要請求獨佔訪問,device.claimInterface
由於在聲明接口時, 數據只能傳輸到接口或相關端點。最後調用device.controlTransferOut
,須要經過適當的命令來設置Arduino設備,經過WebUSB Serial API進行通訊。
從那裏, device.transferIn
對設備執行批量傳輸,通知主機準備好接收批量數據。而後,使用result
包含 必須正確解析的DataView 的對象 來知足諾言 data
。
對於熟悉USB的用戶,全部這些應該看起來很熟悉。
I want moar
The WebUSB API lets you interact with the all USB transfer/endpoint types: WebUSB API容許您與全部USB傳輸/端點類型進行交互:
- CONTROL transfers, used to send or receive configuration or command parameters to a USB device are handled with
controlTransferIn(setup, length)
and controlTransferOut(setup, data)
.
- INTERRUPT transfers, used for a small amount of time sensitive data are handled with the same methods as BULK transfers with
transferIn(endpointNumber, length)
and transferOut(endpointNumber, data)
.
- ISOCHRONOUS transfers, used for streams of data like video and sound are handled with
isochronousTransferIn(endpointNumber, packetLengths)
andisochronousTransferOut(endpointNumber, data, packetLengths)
.
- BULK transfers, used to transfer a large amount of non-time-sensitive data in a reliable way are handled with
transferIn(endpointNumber, length)
andtransferOut(endpointNumber, data)
.
- 控制傳輸,用於發送或接收配置或命令參數到USB設備與處理
controlTransferIn(setup, length)
和 controlTransferOut(setup, data)
。
- 中斷傳輸,用於少許時間敏感數據與批量傳輸處理用相同的方法
transferIn(endpointNumber, length)
和 transferOut(endpointNumber, data)
。
- ISOCHRONOUS傳輸,用於數據流(如視頻和聲音)的ISOCHRONOUS傳輸是用
isochronousTransferIn(endpointNumber, packetLengths)
和處理的 isochronousTransferOut(endpointNumber, data, packetLengths)
。
- BULK傳輸,用於以可靠的方式傳輸大量非時間敏感數據的BULK傳輸是用
transferIn(endpointNumber, length)
和處理的 transferOut(endpointNumber, data)
。
You may also want to have a look at Mike Tsao's WebLight project which provides a ground-up example of building a USB-controlled LED device designed for the WebUSB API (not using an Arduino here). You'll find hardware, software, and firmware.
您可能還想看看Mike Tsao的 WebLight項目 ,該項目爲構建針對WebUSB API(不使用Arduino)的USB控制LED設備提供了一個基礎實例。你會發現硬件,軟件和固件。
Tips
Debugging USB in Chrome is easier with the internal page chrome://device-log
where you can see all USB device related events in one single place.
經過內部頁面chrome://device-log
,您能夠在一個地方查看全部與USB設備相關的事件,在Chrome中調試USB很是方便 。
The internal page chrome://usb-internals
also comes in handy and allows you to simulate connection connection and disconnection of virtual WebUSB devices. This is be useful for doing UI testing without the need for real hardware.
內部頁面 chrome://usb-internals
也派上用場,容許您模擬虛擬WebUSB設備的鏈接鏈接和斷開鏈接。這對於在不須要真實硬件的狀況下進行UI測試是有用的。
On most Linux systems, USB devices are mapped with read-only permissions by default. To allow Chrome to open a USB device, you will need to add a new udev rule. Create a file at /etc/udev/rules.d/50-yourdevicename.rules
with the following content:
SUBSYSTEM=="usb", ATTR{idVendor}=="[yourdevicevendor]", MODE="0664", GROUP="plugdev"
where [yourdevicevendor]
is 2341
if your device is an Arduino for instance.ATTR{idProduct}
can also be added for a more specific rule. Make sure your user
is a member of the plugdev
group. Then, just reconnect your device.
這裏 [yourdevicevendor]
是 2341
若是你的設備是例如一個Arduino。ATTR{idProduct}
也能夠添加更具體的規則。確保您 user
是該plugdev
組的成員 。而後,只需從新鏈接設備。
What's next
A second iteration of the WebUSB API will look at Shared Worker and Service Worker support. Imagine for instance a security key website using the WebUSB API that would install a service worker to act as a middle man to authenticate users.
Note: Microsoft OS 2.0 Descriptors used by the Arduino examples only work on Windows 8.1 and later. Without that Windows support still requires manual installation of an INF file.