WebUSB API ทำให้ USB ปลอดภัยขึ้นและใช้งานได้ง่ายขึ้นด้วยการนำ USB มาใช้ในเว็บ
หากผมบอกง่ายๆ ว่า "USB" ก็เป็นไปได้ที่คุณจะ นึกถึงแป้นพิมพ์ เมาส์ เสียง วิดีโอ และอุปกรณ์จัดเก็บข้อมูลโดยทันที คุณ แต่คุณจะเห็นอุปกรณ์ Universal Serial Bus (USB) ประเภทอื่นๆ จากที่นั่น
อุปกรณ์ USB ที่ไม่ได้มาตรฐานเหล่านี้กำหนดให้ผู้ให้บริการฮาร์ดแวร์เขียนตามแพลตฟอร์มโดยเฉพาะ และ SDK เพื่อให้คุณ (นักพัฒนาซอฟต์แวร์) ได้รับประโยชน์ น่าเสียดายที่รหัสเฉพาะแพลตฟอร์มนี้ได้เคยป้องกันไม่ให้มีการใช้งานอุปกรณ์เหล่านี้ ผ่านเว็บ นั่นเป็นเหตุผลหนึ่งที่เราสร้าง WebUSB API ขึ้น เป็นวิธีการเปิดเผยบริการของอุปกรณ์ USB บนเว็บ สำหรับ API นี้ ฮาร์ดแวร์ จะสร้าง JavaScript SDK ข้ามแพลตฟอร์มสำหรับ อุปกรณ์
แต่สิ่งสำคัญที่สุดคือ การทำให้ USB ปลอดภัยและใช้งานง่ายขึ้นด้วยการ บนเว็บ
มาดูลักษณะการทำงานที่คุณน่าจะได้รับจาก WebUSB API กัน
- ซื้ออุปกรณ์ USB
- เสียบเข้ากับคอมพิวเตอร์ของคุณ การแจ้งเตือนจะปรากฏขึ้นทันที พร้อมกับ เว็บไซต์ที่จะไปสำหรับอุปกรณ์นี้
- คลิกการแจ้งเตือน มีเว็บไซต์พร้อมใช้งานอยู่แล้ว
- คลิกเพื่อเชื่อมต่อ แล้วตัวเลือกอุปกรณ์ USB จะปรากฏใน Chrome ซึ่งคุณสามารถ เลือกอุปกรณ์ของคุณ
นี่ไง!
กระบวนการนี้จะเป็นอย่างไรหากไม่มี WebUSB API
- ติดตั้งแอปพลิเคชันเฉพาะแพลตฟอร์ม
- ถ้าระบบปฏิบัติการของฉันรองรับ ให้ยืนยันว่าฉันได้ดาวน์โหลดแล้ว สิ่งที่เหมาะสม
- ติดตั้งสิ่งนั้น ถ้าคุณโชคดี คุณจะไม่ได้รับข้อความแจ้งของระบบปฏิบัติการหรือป๊อปอัปที่น่ากลัวอีก เตือนให้คุณทราบเกี่ยวกับการติดตั้งไดรเวอร์/แอปพลิเคชันจากอินเทอร์เน็ต ถ้า คุณโชคไม่ดี ไดรเวอร์หรือแอปพลิเคชันที่ติดตั้งทำงานผิดปกติและเป็นอันตราย คอมพิวเตอร์ (โปรดจำไว้ว่า เว็บถูกสร้างขึ้นมาเพื่อควบคุมการทำงานผิดพลาด เว็บไซต์)
- หากคุณใช้ฟีเจอร์นี้เพียงครั้งเดียว รหัสจะยังอยู่ในคอมพิวเตอร์จนกว่าคุณจะ คิดที่จะเอาออก (บนเว็บ พื้นที่ว่างสำหรับ reclaimed.)
ก่อนจะเริ่ม
บทความนี้จะถือว่าคุณมีความรู้พื้นฐานเกี่ยวกับวิธีการทำงานของ USB หากไม่ เราขอแนะนำให้อ่าน USB ใน NutShell สำหรับข้อมูลเบื้องต้นเกี่ยวกับ USB โปรดดูข้อมูลจำเพาะอย่างเป็นทางการของ USB
WebUSB API มีให้บริการใน Chrome 61
พร้อมใช้งานสำหรับช่วงทดลองใช้จากต้นทาง
เพื่อรับความคิดเห็นมากที่สุดเท่าที่จะเป็นไปได้จากนักพัฒนาซอฟต์แวร์ที่ใช้ WebUSB API ที่มีอยู่แล้ว โดยก่อนหน้านี้เราได้เพิ่มฟีเจอร์นี้ใน Chrome 54 และ Chrome 57 เป็นช่วงทดลองใช้จากต้นทาง
ช่วงทดลองใช้ล่าสุดสิ้นสุดไปเรียบร้อยแล้วในเดือนกันยายน 2017
ความเป็นส่วนตัวและความปลอดภัย
HTTPS เท่านั้น
ด้วยประสิทธิภาพของฟีเจอร์นี้ ฟีเจอร์นี้จึงทำงานบนบริบทที่ปลอดภัยเท่านั้น ซึ่งหมายความว่า คุณจะต้องสร้างแอปโดยคำนึงถึง TLS
ต้องมีท่าทางสัมผัสของผู้ใช้
เพื่อรักษาความปลอดภัย navigator.usb.requestDevice()
อาจดำเนินการดังนี้
ผ่านท่าทางสัมผัสของผู้ใช้ เช่น การแตะหรือการคลิกเมาส์
นโยบายสิทธิ์
นโยบายสิทธิ์เป็นกลไกที่ให้นักพัฒนาแอปเลือกเปิดใช้ และปิดใช้ฟีเจอร์เบราว์เซอร์และ API ต่างๆ คุณกำหนดได้ผ่าน HTTP ส่วนหัวและ/หรือ iframe "อนุญาต"
คุณสามารถกำหนดนโยบายสิทธิ์ที่ควบคุมว่าแอตทริบิวต์ usb
จะ
ถูกเปิดเผยในออบเจ็กต์ Navigator หรือกล่าวอีกนัยหนึ่งคือหากคุณอนุญาตให้ใช้ WebUSB
ด้านล่างนี้คือตัวอย่างของนโยบายส่วนหัวที่ไม่อนุญาตให้ใช้ WebUSB
Feature-Policy: fullscreen "*"; usb "none"; payment "self" https://payment.example.com
ด้านล่างนี้คืออีกตัวอย่างหนึ่งของนโยบายคอนเทนเนอร์ที่อนุญาตให้ใช้ USB
<iframe allowpaymentrequest allow="usb; fullscreen"></iframe>
มาเริ่มเขียนโค้ดกันเลย
WebUSB API อาศัย Promises ของ JavaScript เป็นหลัก หากไม่คุ้นเคยกับ
ลองดูบทแนะนำ "คำสัญญา" ที่ยอดเยี่ยมนี้ อีกอย่าง () => {}
ก็คือฟังก์ชันลูกศรของ ECMAScript 2015
รับสิทธิ์เข้าถึงอุปกรณ์ USB
คุณสามารถแจ้งให้ผู้ใช้เลือกอุปกรณ์ USB ที่เชื่อมต่ออยู่เครื่องเดียวโดยใช้
navigator.usb.requestDevice()
หรือโทรติดต่อ navigator.usb.getDevices()
เพื่อรับ
รายการอุปกรณ์ USB ที่เชื่อมต่ออยู่ทั้งหมดที่เว็บไซต์ได้รับสิทธิ์เข้าถึง
ฟังก์ชัน navigator.usb.requestDevice()
ต้องใช้ออบเจ็กต์ JavaScript ที่จำเป็น
ซึ่งอธิบายถึง filters
ตัวกรองเหล่านี้ใช้ในการจับคู่อุปกรณ์ USB ใดๆ กับ
ตัวระบุผู้ให้บริการ (vendorId
) และตัวระบุผลิตภัณฑ์ (productId
) ที่ไม่บังคับ
คีย์ 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 ที่รองรับ
ขนาดแพ็กเก็ตสูงสุด ผู้ให้บริการ และรหัสผลิตภัณฑ์ จำนวนที่เป็นไปได้
การกำหนดค่าที่อุปกรณ์มีได้ โดยพื้นฐานแล้วจะมีทุกช่อง
ตัวบอกทาง 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 เช่นเดียวกับ การกำหนด URL ของหน้า Landing Page แล้ว Chrome จะแสดงการแจ้งเตือนตลอดเวลาเมื่อ เสียบอุปกรณ์ USB แล้ว การคลิกการแจ้งเตือนนี้จะเปิดหน้า Landing Page
คุยกับบอร์ด Arduino
คราวนี้มาดูกันว่าการสื่อสารจาก 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 ที่ฉันใช้อยู่เป็นเพียงการนำไปใช้งาน โปรโตคอลตัวอย่าง 1 รายการ (อิงตามโปรโตคอลอนุกรม 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 ของบุคคลที่สามที่ใช้ในโค้ดตัวอย่างด้านบนนี้ โดยมี 2 สิ่งคือ
- อุปกรณ์นี้ทำหน้าที่เป็นอุปกรณ์ WebUSB ที่ช่วยให้ Chrome อ่าน URL ของหน้า Landing Page ได้
- ระบบจะแสดง WebUSB Serial API ที่คุณอาจใช้เพื่อลบล้างค่าเริ่มต้น
ดูโค้ด JavaScript อีกครั้ง เมื่อได้รับ device
ที่ผู้ใช้เลือกแล้ว
device.open()
เรียกใช้ขั้นตอนเฉพาะแพลตฟอร์มทั้งหมดเพื่อเริ่มเซสชันด้วย USB
อุปกรณ์ จากนั้น ฉันจะต้องเลือกการกำหนดค่า USB ที่พร้อมใช้งานด้วย
device.selectConfiguration()
โปรดทราบว่าการกำหนดค่าจะระบุวิธี
อุปกรณ์ขับเคลื่อน การใช้พลังงานสูงสุด และจำนวนอินเทอร์เฟซ
ถ้าพูดถึงอินเทอร์เฟซ ฉันต้องขอสิทธิ์การเข้าถึงพิเศษด้วย
device.claimInterface()
เนื่องจากสามารถโอนข้อมูลไปยังอินเทอร์เฟซเท่านั้น หรือ
ปลายทางที่เกี่ยวข้องเมื่อมีการอ้างสิทธิ์อินเทอร์เฟซ การโทรครั้งสุดท้าย
ต้องใช้ device.controlTransferOut()
เพื่อตั้งค่าอุปกรณ์ Arduino ด้วย
คำสั่งที่เหมาะสมในการสื่อสารผ่าน WebUSB Serial API
จากนั้น device.transferIn()
จะทำการโอนแบบเป็นกลุ่มไปยัง
เพื่อแจ้งว่าโฮสต์พร้อมที่จะรับข้อมูลจำนวนมากแล้ว จากนั้น
จะมีการทำตามสัญญาด้วยออบเจ็กต์ result
ที่มี DataView data
ที่
จะได้รับการแยกวิเคราะห์อย่างเหมาะสม
หากคุณคุ้นเคยกับ USB ก็น่าจะคุ้นเคยกับสิ่งเหล่านี้
ฉันต้องการมากกว่านี้
WebUSB API ให้คุณโต้ตอบกับการโอน/ปลายทางผ่าน USB ทุกประเภท
- การโอนการควบคุม ซึ่งใช้เพื่อส่งหรือรับการกำหนดค่าหรือคำสั่ง
พารามิเตอร์ไปยังอุปกรณ์ USB ได้รับการจัดการด้วย
controlTransferIn(setup, length)
และcontrolTransferOut(setup, data)
- การโอนภายใน ซึ่งใช้กับข้อมูลที่ละเอียดอ่อนเพียงเล็กน้อย
ใช้วิธีการเดียวกับการโอนจำนวนมาก
transferIn(endpointNumber, length)
และtransferOut(endpointNumber, data)
- การโอนข้อมูล ISOCHRONOUS ซึ่งใช้สำหรับสตรีมข้อมูล เช่น วิดีโอและเสียง
จัดการด้วย
isochronousTransferIn(endpointNumber, packetLengths)
และisochronousTransferOut(endpointNumber, data, packetLengths)
- การโอนแบบ BULK ซึ่งใช้สำหรับโอนข้อมูลจำนวนมากที่ไม่เร่งด่วนใน
วิธีที่เชื่อถือได้จะได้รับการจัดการโดย
transferIn(endpointNumber, length)
และtransferOut(endpointNumber, data)
คุณอาจต้องการดูโครงการ WebLight ของ Mike Tsao ซึ่ง แสดงตัวอย่างพื้นฐานในการสร้างอุปกรณ์ LED ที่ควบคุมด้วย USB ซึ่งออกแบบมา สำหรับ WebUSB API (ไม่ได้ใช้ Arduino ที่นี่) ฮาร์ดแวร์ ซอฟต์แวร์ และเฟิร์มแวร์
เพิกถอนสิทธิ์เข้าถึงอุปกรณ์ USB
เว็บไซต์สามารถล้างสิทธิ์เข้าถึงอุปกรณ์ USB ที่ไม่จำเป็นต้องใช้อีกต่อไป
โดยการเรียกใช้ forget()
ในอินสแตนซ์ USBDevice
ตัวอย่างเช่น สำหรับ
เว็บแอปพลิเคชันด้านการศึกษาซึ่งใช้ในคอมพิวเตอร์ที่ ใช้ร่วมกับอุปกรณ์ต่างๆ
จำนวนของสิทธิ์ที่ผู้ใช้สร้างขึ้นทำให้ผู้ใช้ได้รับประสบการณ์ที่ไม่ดี
// 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 ที่รอดำเนินการ การแบ่งข้อมูลออกเป็นธุรกรรมเล็กๆ และเฉพาะ การส่งครั้งละ 2-3 รายการจะช่วยหลีกเลี่ยงข้อจำกัดเหล่านั้น และยังลด ปริมาณของหน่วยความจำที่ใช้ และอนุญาตให้แอปพลิเคชันของคุณรายงานความคืบหน้า การโอนเสร็จสมบูรณ์
เนื่องจากการโอนหลายรายการที่ส่งไปยังปลายทางจะทำงานตามลำดับเสมอ ปรับปรุงอัตราการส่งข้อมูลได้โดยการส่งข้อมูลบางส่วนที่อยู่ในคิวเพื่อหลีกเลี่ยง เวลาในการตอบสนองระหว่างการโอนผ่าน 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);
}
เคล็ดลับ
การแก้ไขข้อบกพร่อง USB ใน Chrome ทำได้ง่ายขึ้นเมื่อใช้หน้าภายใน about://device-log
ที่คุณสามารถดูเหตุการณ์ทั้งหมดที่เกี่ยวข้องกับอุปกรณ์ 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"
โดย [yourdevicevendor]
จะเป็น 2341
เป็นต้นว่าอุปกรณ์ของคุณเป็น Arduino
คุณเพิ่ม 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 ใน IRC ของ W3C
- รายชื่ออีเมลของ WICG: https://lists.w3.org/Archives/Public/public-wicg/
- โปรเจ็กต์ WebLight: https://github.com/sowbug/weblight
ส่งทวีตไปยัง @ChromiumDev โดยใช้แฮชแท็ก
#WebUSB
และแจ้งให้เราทราบถึงตำแหน่งและวิธีที่คุณใช้งาน
กิตติกรรมประกาศ
ขอขอบคุณ Joe Medley ที่อ่านบทความนี้