WebUSB API membuat USB lebih aman dan mudah digunakan dengan menghadirkannya ke Web.
Jika saya mengatakan "USB" dengan terus terang, kemungkinan besar Anda akan langsung memikirkan keyboard, mouse, audio, video, dan perangkat penyimpanan. Anda benar, tetapi Anda akan menemukan jenis perangkat {i>Universal Serial Bus<i} (USB) lain di luar sana.
Perangkat USB non-standar ini mengharuskan vendor hardware menulis driver dan SDK khusus platform agar Anda (developer) dapat memanfaatkannya. Sayangnya, kode khusus platform ini secara historis telah mencegah perangkat ini digunakan oleh Web. Dan itulah salah satu alasan WebUSB API dibuat: untuk memberikan cara mengekspos layanan perangkat USB ke Web. Dengan API ini, produsen hardware akan dapat membangun SDK JavaScript lintas platform untuk perangkat mereka.
Namun, yang paling penting, hal ini akan membuat USB lebih aman dan mudah digunakan dengan membawanya ke Web.
Mari kita lihat perilaku yang dapat Anda harapkan dengan WebUSB API:
- Beli perangkat USB.
- Colokkan ke komputer. Notifikasi akan langsung muncul, dengan situs yang tepat untuk dibuka di perangkat ini.
- Klik notifikasi. Situs sudah ada dan siap digunakan.
- Klik untuk menghubungkan dan pemilih perangkat USB akan muncul di Chrome tempat Anda dapat memilih perangkat.
Tada!
Seperti apa prosedur ini tanpa WebUSB API?
- Menginstal aplikasi khusus platform.
- Jika aplikasi tersebut didukung di sistem operasi saya, pastikan saya telah mendownload aplikasi yang tepat.
- Instal aplikasi tersebut. Jika beruntung, Anda tidak akan mendapatkan perintah OS atau pop-up yang menakutkan yang memperingatkan Anda tentang penginstalan driver/aplikasi dari internet. Jika Anda tidak beruntung, driver atau aplikasi yang diinstal akan mengalami malfungsi dan membahayakan komputer Anda. (Ingat, web dibuat untuk berisi situs yang tidak berfungsi dengan baik).
- Jika Anda hanya menggunakan fitur ini satu kali, kode akan tetap berada di komputer sampai Anda berpikir untuk menghapusnya. (Di Web, ruang untuk yang tidak digunakan pada akhirnya akan diambil kembali.)
Sebelum memulai
Artikel ini mengasumsikan bahwa Anda memiliki pengetahuan dasar tentang cara kerja USB. Jika belum, sebaiknya baca USB di NutShell. Untuk informasi latar belakang tentang USB, lihat spesifikasi USB resmi.
WebUSB API tersedia di Chrome 61.
Tersedia untuk uji coba origin
Untuk mendapatkan masukan sebanyak mungkin dari developer yang menggunakan WebUSB API di lapangan, kami sebelumnya telah menambahkan fitur ini di Chrome 54 dan Chrome 57 sebagai uji coba origin.
Uji coba terbaru telah berhasil berakhir pada September 2017.
Privasi dan keamanan
Khusus HTTPS
Karena kecanggihan fitur ini, fitur ini hanya berfungsi pada konteks yang aman. Artinya, Anda harus mem-build dengan mempertimbangkan TLS.
Diperlukan gestur pengguna
Sebagai tindakan pengamanan, navigator.usb.requestDevice()
hanya dapat
dipanggil melalui gestur pengguna seperti sentuhan atau klik mouse.
Kebijakan Izin
Kebijakan Izin adalah mekanisme yang memungkinkan developer mengaktifkan dan menonaktifkan berbagai API dan fitur browser secara selektif. Izin ini dapat ditentukan melalui header HTTP dan/atau atribut "izinkan" iframe.
Anda dapat menentukan Kebijakan Izin yang mengontrol apakah atribut usb
ditampilkan di objek Navigator, atau dengan kata lain, apakah Anda mengizinkan WebUSB.
Berikut adalah contoh kebijakan header yang tidak mengizinkan WebUSB:
Feature-Policy: fullscreen "*"; usb "none"; payment "self" https://payment.example.com
Berikut adalah contoh lain kebijakan penampung yang mengizinkan USB:
<iframe allowpaymentrequest allow="usb; fullscreen"></iframe>
Mari mulai coding
WebUSB API sangat bergantung pada Promise JavaScript. Jika Anda belum memahaminya, lihat tutorial Promise yang menarik ini. Satu hal lagi, () => {}
hanyalah Fungsi Panah ECMAScript 2015.
Mendapatkan akses ke perangkat USB
Anda dapat meminta pengguna untuk memilih satu perangkat USB yang terhubung menggunakan
navigator.usb.requestDevice()
atau memanggil navigator.usb.getDevices()
untuk mendapatkan
daftar semua perangkat USB terhubung yang telah diberikan akses ke situs.
Fungsi navigator.usb.requestDevice()
menggunakan objek JavaScript wajib
yang menentukan filters
. Filter ini digunakan untuk mencocokkan perangkat USB dengan
ID vendor (vendorId
) dan, secara opsional, ID produk (productId
) tertentu.
Kunci classCode
, protocolCode
, serialNumber
, dan subclassCode
juga dapat
ditentukan di sana.
Misalnya, berikut cara mendapatkan akses ke perangkat Arduino yang terhubung dan dikonfigurasi untuk mengizinkan origin.
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); });
Sebelum Anda bertanya, saya tidak secara ajaib mendapatkan angka heksadesimal
0x2341
ini. Saya cukup menelusuri kata "Arduino" di Daftar ID USB ini.
device
USB yang ditampilkan dalam promise yang terpenuhi di atas memiliki beberapa informasi dasar tetapi penting tentang perangkat, seperti versi USB yang didukung, ukuran paket maksimum, vendor, dan ID produk, serta jumlah kemungkinan konfigurasi yang dapat dimiliki perangkat. Pada dasarnya, file ini berisi semua kolom dalam
Deskripsi USB perangkat.
// 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"
});
})
Selain itu, jika perangkat USB mengumumkan dukungannya untuk WebUSB, serta menentukan URL halaman landing, Chrome akan menampilkan notifikasi persisten saat perangkat USB dicolokkan. Mengklik notifikasi ini akan membuka halaman landing.
Berbicara dengan board USB Arduino
Baiklah, sekarang mari kita lihat betapa mudahnya berkomunikasi dari papan Arduino yang kompatibel dengan WebUSB melalui port USB. Lihat petunjuk di https://github.com/webusb/arduino untuk mengaktifkan sketsel Anda dengan WebUSB.
Jangan khawatir, saya akan membahas semua metode perangkat WebUSB yang disebutkan di bawah nanti dalam artikel ini.
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); });
Perlu diingat bahwa library WebUSB yang saya gunakan hanya menerapkan satu protokol contoh (berdasarkan protokol serial USB standar) dan bahwa produsen dapat membuat kumpulan dan jenis endpoint apa pun yang mereka inginkan. Transfer kontrol sangat cocok untuk perintah konfigurasi kecil karena mendapatkan prioritas bus dan memiliki struktur yang ditentukan dengan baik.
Dan berikut adalah sketsa yang telah diupload ke board 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.
}
Library Arduino WebUSB pihak ketiga yang digunakan dalam kode contoh di atas pada dasarnya melakukan dua hal:
- Perangkat ini berfungsi sebagai perangkat WebUSB yang memungkinkan Chrome membaca URL halaman landing.
- API ini mengekspos WebUSB Serial API yang dapat Anda gunakan untuk mengganti API default.
Lihat kembali kode JavaScript. Setelah saya memilih device
oleh pengguna,
device.open()
menjalankan semua langkah khusus platform untuk memulai sesi dengan perangkat
USB. Kemudian, saya harus memilih Konfigurasi USB yang tersedia dengan
device.selectConfiguration()
. Ingat bahwa konfigurasi menentukan cara
perangkat diberi daya, konsumsi daya maksimumnya, dan jumlah antarmukanya.
Terkait antarmuka, saya juga perlu meminta akses eksklusif dengan
device.claimInterface()
karena data hanya dapat ditransfer ke antarmuka atau
endpoint terkait saat antarmuka diklaim. Terakhir, memanggil
device.controlTransferOut()
diperlukan untuk menyiapkan perangkat Arduino dengan
perintah yang sesuai untuk berkomunikasi melalui WebUSB Serial API.
Dari sana, device.transferIn()
melakukan transfer massal ke
perangkat untuk memberi tahu bahwa host siap menerima data massal. Kemudian, promise akan dipenuhi dengan objek result
yang berisi data
DataView yang harus diurai dengan benar.
Jika Anda sudah terbiasa dengan USB, semua ini akan terlihat cukup familier.
Saya ingin lebih banyak
WebUSB API memungkinkan Anda berinteraksi dengan semua jenis transfer/endpoint USB:
- Transfer KONTROL, yang digunakan untuk mengirim atau menerima parameter konfigurasi atau perintah
ke perangkat USB, ditangani dengan
controlTransferIn(setup, length)
dancontrolTransferOut(setup, data)
. - Transfer INTERRUPT, yang digunakan untuk data sensitif waktu dalam jumlah kecil, ditangani dengan metode yang sama seperti transfer BULK dengan
transferIn(endpointNumber, length)
dantransferOut(endpointNumber, data)
. - Transfer ISOCHRONOUS, yang digunakan untuk streaming data seperti video dan suara, ditangani dengan
isochronousTransferIn(endpointNumber, packetLengths)
danisochronousTransferOut(endpointNumber, data, packetLengths)
. - Transfer BULK, yang digunakan untuk mentransfer data non-sensitif waktu dalam jumlah besar dengan cara yang andal, ditangani dengan
transferIn(endpointNumber, length)
dantransferOut(endpointNumber, data)
.
Anda juga dapat melihat project WebLight Mike Tsao yang memberikan contoh dasar pembuatan perangkat LED yang dikontrol USB yang dirancang untuk WebUSB API (tidak menggunakan Arduino di sini). Anda akan menemukan perangkat keras, perangkat lunak, dan {i>firmware<i}.
Mencabut akses ke perangkat USB
Situs dapat membersihkan izin untuk mengakses perangkat USB yang tidak lagi diperlukan
dengan memanggil forget()
pada instance USBDevice
. Misalnya, untuk
aplikasi web pendidikan yang digunakan di komputer bersama dengan banyak perangkat, sejumlah besar
izin yang dibuat pengguna secara kumulatif akan memberikan pengalaman pengguna yang buruk.
// Voluntarily revoke access to this USB device.
await device.forget();
Karena forget()
tersedia di Chrome 101 atau yang lebih baru, periksa apakah fitur ini
didukung dengan hal berikut:
if ("usb" in navigator && "forget" in USBDevice.prototype) {
// forget() is supported.
}
Batas ukuran transfer
Beberapa sistem operasi memberlakukan batasan jumlah data yang dapat menjadi bagian dari transaksi USB yang tertunda. Membagi data menjadi transaksi yang lebih kecil dan hanya mengirimkan beberapa transaksi sekaligus akan membantu menghindari batasan tersebut. Hal ini juga mengurangi jumlah memori yang digunakan dan memungkinkan aplikasi Anda melaporkan progres saat transfer selesai.
Karena beberapa transfer yang dikirim ke endpoint selalu dieksekusi secara berurutan, throughput dapat ditingkatkan dengan mengirimkan beberapa bagian yang diantrekan untuk menghindari latensi di antara transfer USB. Setiap kali sebuah potongan dikirimkan secara lengkap, kode tersebut akan memberi tahu Anda bahwa kode tersebut harus menyediakan lebih banyak data seperti yang didokumentasikan dalam contoh fungsi helper di bawah ini.
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);
}
Tips
Proses debug USB di Chrome menjadi lebih mudah dengan halaman internal about://device-log
tempat Anda dapat melihat semua peristiwa terkait perangkat USB di satu tempat.
Halaman internal about://usb-internals
juga berguna dan memungkinkan Anda
menyimulasikan koneksi dan pemutusan koneksi perangkat WebUSB virtual.
Hal ini berguna untuk melakukan pengujian UI tanpa hardware sungguhan.
Pada sebagian besar sistem Linux, perangkat USB dipetakan dengan izin hanya baca secara
default. Agar Chrome dapat membuka perangkat USB, Anda harus menambahkan aturan
udev baru. Buat file di /etc/udev/rules.d/50-yourdevicename.rules
dengan
konten berikut:
SUBSYSTEM=="usb", ATTR{idVendor}=="[yourdevicevendor]", MODE="0664", GROUP="plugdev"
dengan [yourdevicevendor]
adalah 2341
jika perangkat Anda adalah Arduino, misalnya.
ATTR{idProduct}
juga dapat ditambahkan untuk aturan yang lebih spesifik. Pastikan
user
Anda adalah anggota grup plugdev
. Kemudian, hubungkan kembali perangkat Anda.
Resource
- Stack Overflow: https://stackoverflow.com/questions/tagged/webusb
- Spesifikasi WebUSB API: http://wicg.github.io/webusb/
- Status Fitur Chrome: https://www.chromestatus.com/feature/5651917954875392
- Masalah Spesifikasi: https://github.com/WICG/webusb/issues
- Bug Implementasi: http://crbug.com?q=component:Blink>USB
- WebUSB ❤ ️Arduino: https://github.com/webusb/arduino
- IRC: #webusb di IRC W3C
- Milis WICG: https://lists.w3.org/Archives/Public/public-wicg/
- Project WebLight: https://github.com/sowbug/weblight
Kirim tweet ke @ChromiumDev menggunakan hashtag
#WebUSB
dan beri tahu kami tempat dan cara Anda menggunakannya.
Ucapan terima kasih
Terima kasih kepada Joe Medley yang telah meninjau artikel ini.