WebUSB API giúp USB an toàn và dễ sử dụng hơn bằng cách đưa ứng dụng này lên web.
Nếu tôi nói rõ ràng và đơn giản là "USB", thì có khả năng bạn sẽ nghĩ ngay đến bàn phím, chuột, âm thanh, video và thiết bị lưu trữ. Bạn đúng, nhưng bạn sẽ tìm thấy các loại thiết bị Universal Serial Bus (USB) khác ở đó.
Những thiết bị USB không chuẩn hoá này yêu cầu các nhà cung cấp phần cứng phải ghi dữ liệu dành riêng cho từng nền tảng trình điều khiển và SDK để bạn (nhà phát triển) tận dụng các trình điều khiển đó. Rất tiếc, mã dành riêng cho nền tảng này trước đây đã ngăn không cho các thiết bị này sử dụng trên web. Và đó là một trong những lý do khiến WebUSB API ra đời: để cung cấp cách hiển thị dịch vụ thiết bị USB lên Web. Với API này, phần cứng nhà sản xuất có thể tạo SDK JavaScript đa nền tảng cho thiết bị.
Nhưng quan trọng nhất là việc này sẽ giúp USB an toàn và dễ sử dụng hơn nhờ mang ứng dụng lên Web.
Hãy xem hành vi bạn có thể mong đợi với API WebUSB:
- Mua thiết bị USB.
- Cắm thiết bị vào máy tính. Một thông báo sẽ xuất hiện ngay lập tức ở bên phải truy cập trên trang web nào cho thiết bị này.
- Nhấp vào thông báo đó. Trang web đã sẵn sàng để bạn sử dụng!
- Nhấp để kết nối và trình chọn thiết bị USB sẽ hiển thị trong Chrome nơi bạn có thể chọn thiết bị của bạn.
Tada!
Quy trình này sẽ như thế nào nếu không có API WebUSB?
- Cài đặt ứng dụng dành riêng cho nền tảng.
- Nếu tệp này thậm chí còn được hỗ trợ trên hệ điều hành của tôi, hãy xác minh rằng tôi đã tải xuống nội dung phù hợp.
- Cài đặt. Nếu may mắn, bạn sẽ không nhận được cửa sổ bật lên hay lời nhắc hệ điều hành đáng sợ nào cảnh báo bạn về việc cài đặt trình điều khiển/ứng dụng qua Internet. Nếu bạn không may mắn, trình điều khiển hoặc ứng dụng đã cài đặt sẽ gặp trục trặc và gây hại máy tính của bạn. (Hãy nhớ rằng web được xây dựng để ngăn chặn những trục trặc ).
- Nếu bạn chỉ sử dụng tính năng này một lần, mã sẽ được lưu lại trên máy tính cho đến khi bạn hãy nghĩ đến việc xoá nội dung đó. (Trên web, không gian chưa được sử dụng cuối cùng sẽ là reclaimed.)
Trước khi tôi bắt đầu
Bài viết này giả định bạn đã có một số kiến thức cơ bản về cách hoạt động của USB. Nếu không, tôi bạn nên đọc USB trong NutShell. Để biết thông tin cơ bản về USB, hãy xem thông số kỹ thuật chính thức của USB.
API WebUSB có trong Chrome 61.
Có cho bản dùng thử theo nguyên gốc
Để nhận được nhiều phản hồi nhất có thể từ các nhà phát triển sử dụng WebUSB API mà chúng tôi đề cập, trước đây chúng tôi đã thêm tính năng này vào Chrome 54 và Chrome 57 dưới dạng bản dùng thử theo nguyên gốc.
Thử nghiệm mới nhất đã kết thúc thành công vào tháng 9 năm 2017.
Quyền riêng tư và bảo mật
Chỉ HTTPS
Do sức mạnh của tính năng này nên tính năng chỉ hoạt động trên bối cảnh an toàn. Điều này có nghĩa là bạn sẽ phải tạo giao thức có lưu ý đến TLS.
Yêu cầu cử chỉ của người dùng
Để đề phòng vấn đề bảo mật, navigator.usb.requestDevice()
có thể chỉ
được gọi thông qua một cử chỉ của người dùng, chẳng hạn như chạm hoặc nhấp chuột.
Chính sách về quyền
Chính sách về quyền là một cơ chế cho phép nhà phát triển chọn cấp quyền cũng như tắt nhiều tính năng và API của trình duyệt. Có thể xác định URL này qua HTTP tiêu đề và/hoặc iframe "cho phép" .
Bạn có thể xác định Chính sách về quyền kiểm soát việc thuộc tính usb
có phải là
hiển thị trên đối tượng Navigator hoặc nói cách khác nếu bạn cho phép WebUSB.
Dưới đây là ví dụ về một chính sách tiêu đề không cho phép WebUSB:
Feature-Policy: fullscreen "*"; usb "none"; payment "self" https://payment.example.com
Dưới đây là một ví dụ khác về chính sách vùng chứa cho phép USB:
<iframe allowpaymentrequest allow="usb; fullscreen"></iframe>
Hãy bắt đầu lập trình
WebUSB API chủ yếu dựa vào Promise (Lời hứa) của JavaScript. Nếu bạn không quen thuộc
với họ, hãy xem hướng dẫn về các lời hứa tuyệt vời này. Một điều nữa, () => {}
chỉ đơn giản là các hàm mũi tên ECMAScript 2015.
Truy cập vào thiết bị USB
Bạn có thể nhắc người dùng chọn một thiết bị USB đã kết nối bằng cách sử dụng
navigator.usb.requestDevice()
hoặc gọi navigator.usb.getDevices()
để nhận
danh sách tất cả thiết bị USB đã kết nối mà trang web đã được cấp quyền truy cập.
Hàm navigator.usb.requestDevice()
nhận đối tượng JavaScript bắt buộc
xác định filters
. Các bộ lọc này được sử dụng để khớp bất kỳ thiết bị USB nào có
nhà cung cấp (vendorId
) và mã nhận dạng sản phẩm (productId
) (không bắt buộc).
Các khoá classCode
, protocolCode
, serialNumber
và subclassCode
có thể
cũng được xác định ở đó.
Ví dụ: dưới đây là cách truy cập vào một thiết bị Arduino đã kết nối đã định cấu hình để cho phép nguồn gốc.
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); });
Trước khi bạn hỏi, tôi chưa từng nghĩ ra 0x2341
hệ thập lục phân này
số. Tôi chỉ tìm kiếm từ "Arduino" trong Danh sách mã USB này.
USB device
được trả về trong lời hứa đã thực hiện ở trên có một số tính năng cơ bản
thông tin quan trọng về thiết bị, chẳng hạn như phiên bản USB được hỗ trợ,
kích thước gói tối đa, mã nhà cung cấp và mã sản phẩm, số lượng gói tin có thể
các cấu hình mà thiết bị có thể có. Về cơ bản, lớp này chứa tất cả các trường trong
Trình mô tả USB của thiết bị.
// 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"
});
})
Nhân tiện, nếu thiết bị USB thông báo hỗ trợ WebUSB, cũng như xác định URL trang đích, Chrome sẽ hiển thị thông báo liên tục khi Đã cắm thiết bị USB. Khi bạn nhấp vào thông báo này, trang đích sẽ mở ra.
Nói chuyện với bo mạch USB Arduino
Được rồi, bây giờ hãy xem việc giao tiếp từ một thiết bị tương thích WebUSB dễ dàng như thế nào Bảng mạch Arduino trên cổng USB. Xem hướng dẫn tại https://github.com/webusb/arduino để WebUSB cho phép các bản phác thảo của bạn.
Đừng lo, tôi sẽ đề cập đến tất cả phương thức thiết bị WebUSB được đề cập bên dưới ở phần sau bài viết này.
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); });
Xin lưu ý rằng thư viện WebUSB mà tôi đang sử dụng chỉ đang triển khai một giao thức mẫu (dựa trên giao thức nối tiếp USB chuẩn) và nhà sản xuất có thể tạo bất kỳ tập hợp và loại điểm cuối nào họ muốn. Chuyển quyền kiểm soát đặc biệt phù hợp với các lệnh cấu hình nhỏ như chúng được ưu tiên xe buýt và có cấu trúc được xác định rõ ràng.
Và đây là bản phác thảo đã được tải lên bảng 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.
}
Thư viện WebUSB Arduino của bên thứ ba được sử dụng trong mã mẫu ở trên về cơ bản có hai điều:
- Thiết bị này hoạt động như một thiết bị WebUSB cho phép Chrome đọc URL trang đích.
- Thao tác này sẽ hiển thị một API WebUSB Serial mà bạn có thể dùng để ghi đè API mặc định.
Xem lại mã JavaScript. Sau khi nhận được device
do người dùng chọn,
device.open()
thực hiện tất cả các bước dành riêng cho từng nền tảng để bắt đầu phiên bằng USB
thiết bị. Sau đó, tôi phải chọn một Cấu hình USB có sẵn
device.selectConfiguration()
. Hãy nhớ rằng cấu hình chỉ định cách
thiết bị được cấp nguồn, mức tiêu thụ điện năng tối đa và số lượng giao diện của thiết bị.
Nói về giao diện, tôi cũng cần yêu cầu quyền truy cập độc quyền với
device.claimInterface()
vì dữ liệu chỉ có thể được chuyển sang giao diện hoặc
các điểm cuối được liên kết khi giao diện được xác nhận quyền sở hữu. Cuối cùng cũng gọi được
Cần có device.controlTransferOut()
để thiết lập thiết bị Arduino bằng
các lệnh thích hợp để giao tiếp qua WebUSB Serial API.
Tại đó, device.transferIn()
sẽ thực hiện việc chuyển hàng loạt vào
để thông báo rằng máy chủ lưu trữ đã sẵn sàng nhận dữ liệu hàng loạt. Sau đó,
Hứa hẹn được thực hiện bằng đối tượng result
chứa DataView data
phải được phân tích cú pháp một cách thích hợp.
Nếu bạn đã quen thuộc với USB thì tất cả những tính năng này trông khá quen thuộc.
Tôi muốn nhiều hơn
WebUSB API cho phép bạn tương tác với tất cả các loại điểm cuối/truyền qua USB:
- Kiểm soát các lệnh chuyển, dùng để gửi hoặc nhận cấu hình hoặc lệnh
các tham số vào thiết bị USB, được xử lý bằng
controlTransferIn(setup, length)
vàcontrolTransferOut(setup, data)
. - Ngắt kết nối, dùng cho một lượng nhỏ dữ liệu có giới hạn thời gian,
được xử lý bằng các phương thức tương tự như chuyển BULK bằng
transferIn(endpointNumber, length)
vàtransferOut(endpointNumber, data)
. - Truyền ISOCHRONOUS, được sử dụng cho các luồng dữ liệu như video và âm thanh, được
được xử lý với
isochronousTransferIn(endpointNumber, packetLengths)
vàisochronousTransferOut(endpointNumber, data, packetLengths)
- nhiều lượt chuyển, được dùng để chuyển một lượng lớn dữ liệu không có giới hạn thời gian trong
một cách đáng tin cậy, được xử lý bằng
transferIn(endpointNumber, length)
vàtransferOut(endpointNumber, data)
Bạn cũng nên xem dự án WebLight của Mike Tsao cung cấp ví dụ từ đầu về cách xây dựng thiết bị LED được điều khiển bằng USB được thiết kế cho WebUSB API (không sử dụng Arduino tại đây). Bạn sẽ thấy phần cứng, phần mềm và chương trình cơ sở.
Thu hồi quyền truy cập vào thiết bị USB
Trang web này có thể dọn dẹp quyền truy cập vào thiết bị USB mà trang web không cần nữa
bằng cách gọi forget()
trên thực thể USBDevice
. Ví dụ: để có một
ứng dụng web giáo dục được dùng trên một máy tính dùng chung với nhiều thiết bị,
số lượng quyền tích luỹ do người dùng tạo sẽ tạo ra trải nghiệm kém cho người dùng.
// Voluntarily revoke access to this USB device.
await device.forget();
Vì forget()
có trong Chrome 101 trở lên, hãy kiểm tra xem tính năng này có
được hỗ trợ bằng các tính năng sau:
if ("usb" in navigator && "forget" in USBDevice.prototype) {
// forget() is supported.
}
Giới hạn về số tiền chuyển
Một số hệ điều hành đặt ra giới hạn về lượng dữ liệu có thể được đưa vào các giao dịch USB đang chờ xử lý. Chia dữ liệu của bạn thành các giao dịch nhỏ hơn và chỉ gửi một vài thư cùng lúc sẽ giúp tránh những hạn chế đó. Đồng thời, mức bộ nhớ đã sử dụng và cho phép ứng dụng của bạn báo cáo tiến trình dưới dạng quá trình chuyển đã hoàn tất.
Do nhiều lượt chuyển được gửi tới một điểm cuối luôn thực thi theo thứ tự, nên có thể cải thiện thông lượng bằng cách gửi nhiều đoạn hàng đợi để tránh độ trễ giữa các lần chuyển USB. Mỗi khi một đoạn được truyền đầy đủ, thông báo cho mã của bạn biết rằng mã đó sẽ cung cấp thêm dữ liệu như được ghi trong trình trợ giúp ví dụ về hàm bên dưới.
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);
}
Mẹo
Gỡ lỗi USB trong Chrome dễ dàng hơn với trang nội bộ about://device-log
nơi bạn có thể xem tất cả sự kiện liên quan đến thiết bị USB ở cùng một nơi.
Trang nội bộ about://usb-internals
cũng rất hữu ích và cho phép bạn
để mô phỏng quá trình kết nối và ngắt kết nối các thiết bị WebUSB ảo.
Điều này rất hữu ích khi kiểm thử giao diện người dùng mà không cần phần cứng thực.
Trên hầu hết các hệ thống Linux, thiết bị USB được ánh xạ với quyền chỉ đọc bằng cách
mặc định. Để cho phép Chrome mở thiết bị USB, bạn cần thêm một udev mới
. Tạo một tệp tại /etc/udev/rules.d/50-yourdevicename.rules
bằng phần tử
nội dung sau:
SUBSYSTEM=="usb", ATTR{idVendor}=="[yourdevicevendor]", MODE="0664", GROUP="plugdev"
trong đó [yourdevicevendor]
là 2341
nếu thiết bị của bạn là Arduino.
Bạn cũng có thể thêm ATTR{idProduct}
cho quy tắc cụ thể hơn. Hãy đảm bảo rằng
user
là thành viên của nhóm plugdev
. Sau đó, bạn chỉ cần kết nối lại thiết bị.
Tài nguyên
- Stack Overflow: https://stackoverflow.com/questions/tagged/webusb
- Thông số kỹ thuật API WebUSB: http://wicg.github.io/webusb/
- Trạng thái tính năng của Chrome: https://www.chromestatus.com/feature/5651917954875392
- Các vấn đề về quy cách: https://github.com/WICG/webusb/issues
- Lỗi triển khai: http://crbug.com?q=component:Blink>USB
- WebUSB ❤ ️Arduino: https://github.com/webusb/arduino
- IRC: #webusb trên IRC của W3C
- Danh sách gửi thư của WICG: https://lists.w3.org/Archives/Public/public-wicg/
- Dự án WebLight: https://github.com/sowbug/weblight
Gửi một bài đăng đến @ChromiumDev kèm theo hashtag
#WebUSB
đồng thời cho chúng tôi biết bạn đang sử dụng ở đâu và như thế nào.
Xác nhận
Cảm ơn Joe Medley đã đánh giá bài viết này.