वेब पर यूएसबी डिवाइसों को ऐक्सेस करना

WebUSB API, उसे वेब पर लाकर, यूएसबी को ज़्यादा सुरक्षित और आसान बनाता है.

François Beaufort
François Beaufort

अगर मैंने साफ़ शब्दों में कहा, तो "यूएसबी", इसकी संभावना है कि आपको कीबोर्ड, माउस, ऑडियो, वीडियो, और स्टोरेज डिवाइस के बारे में तुरंत सोचना पड़े. तुम सही थी, लेकिन आपको दूसरी तरह के यूनिवर्सल सीरियल बस (यूएसबी) डिवाइस मिल जाएँगे.

इन नॉन-स्टैंडर्ड यूएसबी डिवाइसों को हार्डवेयर वेंडर के लिए ज़रूरी है कि वे प्लैटफ़ॉर्म के हिसाब से ड्राइवर और SDK टूल तैयार करें, ताकि आप (डेवलपर) इनका फ़ायदा ले सकें. दुख की बात यह है कि प्लैटफ़ॉर्म के इस खास कोड की वजह से, अब तक वेब पर इन डिवाइसों को इस्तेमाल करने से रोका गया है. WebUSB API को इसी वजह से बनाया गया है. इसकी मदद से, यूएसबी डिवाइस की सेवाओं को वेब पर एक तरीके से दिखाया जा सकता है. इस एपीआई की मदद से, हार्डवेयर बनाने वाली कंपनियां अपने डिवाइसों के लिए, क्रॉस-प्लैटफ़ॉर्म JavaScript SDK टूल बना सकेंगी.

हालांकि, यह सबसे ज़रूरी बात है कि इससे यूएसबी को वेब पर लाया जा सकता है और इसे इस्तेमाल करना ज़्यादा सुरक्षित और आसान हो जाएगा.

आइए देखते हैं कि WebUSB API से, किस तरह के व्यवहार की उम्मीद की जा सकती है:

  1. यूएसबी डिवाइस खरीदें.
  2. इसे अपने कंप्यूटर से कनेक्ट करें. इसके तुरंत बाद, आपको एक सूचना दिखेगी. साथ ही, आपको इस डिवाइस से जुड़ी सही वेबसाइट पर जाने का विकल्प भी मिलेगा.
  3. सूचना पर क्लिक करें. वेबसाइट तैयार है और अब इसका इस्तेमाल किया जा सकता है!
  4. कनेक्ट करने के लिए क्लिक करें और Chrome में यूएसबी डिवाइस चुनने का विकल्प दिखेगा, जहां से अपना डिवाइस चुना जा सकता है.

टाडा!

WebUSB API के बिना, यह प्रोसेस कैसी होगी?

  1. प्लैटफ़ॉर्म के हिसाब से ऐप्लिकेशन इंस्टॉल करें.
  2. अगर वह मेरे ऑपरेटिंग सिस्टम पर भी काम करता है, तो देख लें कि मैंने सही डाउनलोड किया है.
  3. डिवाइस इंस्टॉल करें. अगर आपकी किस्मत अच्छी है, तो आपको इंटरनेट से ड्राइवर/ऐप्लिकेशन इंस्टॉल करने के बारे में कोई भी डरावना ओएस प्रॉम्प्ट या पॉप-अप चेतावनी नहीं मिलेगी. अगर आपकी किस्मत अच्छी नहीं है, तो स्थापित ड्राइवर या ऐप्लिकेशन ख़राब हो जाते हैं और आपके कंप्यूटर को नुकसान पहुंचाते हैं. (ध्यान रखें कि वेब को इस तरह बनाया गया है कि उसमें खराबी वाली वेबसाइटें मौजूद हों).
  4. अगर इस सुविधा का इस्तेमाल सिर्फ़ एक बार किया जाता है, तो कोड आपके कंप्यूटर पर तब तक बना रहता है, जब तक आप उसे हटाना न चाहें. (वेब पर, 'इस्तेमाल न किए गए कॉन्टेंट' के लिए बची जगह को वापस पाया जाता है.)

शुरू करने से पहले

इस लेख में यह माना जाएगा कि आपको यूएसबी के काम करने के तरीके की कुछ बुनियादी जानकारी है. अगर नहीं, तो मेरा सुझाव है कि आप NutShell में यूएसबी पढ़ें. यूएसबी के बारे में बैकग्राउंड की जानकारी के लिए, यूएसबी की आधिकारिक विशेषताएं देखें.

WebUSB API, Chrome 61 में उपलब्ध है.

ऑरिजिन ट्रायल के लिए उपलब्ध है

फ़ील्ड में WebUSB API का इस्तेमाल करने वाले डेवलपर से ज़्यादा से ज़्यादा सुझाव पाने के लिए, हमने पहले ही इस सुविधा को Chrome 54 और Chrome 57 में ऑरिजिन ट्रायल के तौर पर जोड़ा था.

आखिरी बार मुफ़्त में आज़माने की अवधि सितंबर 2017 में खत्म हो गई है.

निजता और सुरक्षा

सिर्फ़ एचटीटीपीएस

इस सुविधा की खासियत की वजह से, यह सिर्फ़ सुरक्षित कॉन्टेक्स्ट पर काम करती है. इसका मतलब है कि आपको TLS को ध्यान में रखकर बनाना होगा.

उपयोगकर्ता जेस्चर ज़रूरी है

सुरक्षा के लिहाज़ से, navigator.usb.requestDevice() को सिर्फ़ उपयोगकर्ता के जेस्चर (हाव-भाव) से ही कॉल किया जा सकता है. जैसे, टच या माउस क्लिक.

अनुमतियों से जुड़ी नीति

अनुमतियों से जुड़ी नीति एक ऐसी तकनीक है जो डेवलपर को ब्राउज़र की कई सुविधाओं और एपीआई को अपनी पसंद के हिसाब से चालू और बंद करने की सुविधा देती है. इसे एचटीटीपी हेडर और/या iframe "अनुमति दें" एट्रिब्यूट की मदद से तय किया जा सकता है.

अनुमतियों से जुड़ी नीति तय की जा सकती है. इससे यह कंट्रोल किया जा सकता है कि usb एट्रिब्यूट, नेविगेटर ऑब्जेक्ट में दिखे या दूसरे शब्दों में, अगर आपने WebUSB को अनुमति दी हो.

नीचे हेडर नीति का एक उदाहरण दिया गया है, जिसमें WebUSB की अनुमति नहीं है:

Feature-Policy: fullscreen "*"; usb "none"; payment "self" https://payment.example.com

नीचे कंटेनर नीति का एक और उदाहरण दिया गया है, जिसमें यूएसबी की अनुमति है:

<iframe allowpaymentrequest allow="usb; fullscreen"></iframe>

कोडिंग शुरू करते हैं

WebUSB API, JavaScript के प्रॉमिस पर काफ़ी निर्भर करता है. अगर आपको इनके बारे में नहीं पता है, तो प्रॉमिसेस का यह शानदार ट्यूटोरियल देखें. एक और बात, () => {} सिर्फ़ ECMAScript 2015 के ऐरो फ़ंक्शन हैं.

यूएसबी डिवाइसों का ऐक्सेस पाएं

ऐसे में, उपयोगकर्ता से navigator.usb.requestDevice() का इस्तेमाल करके, कनेक्ट किया गया एक यूएसबी डिवाइस चुनने के लिए कहा जा सकता है. इसके अलावा, navigator.usb.getDevices() पर कॉल करके, कनेक्ट किए गए उन सभी यूएसबी डिवाइसों की सूची देखी जा सकती है जिनका ऐक्सेस वेबसाइट को दिया गया है.

navigator.usb.requestDevice() फ़ंक्शन एक ज़रूरी JavaScript ऑब्जेक्ट लेता है. यह ऑब्जेक्ट filters के बारे में जानकारी देता है. इन फ़िल्टर का इस्तेमाल किसी भी यूएसबी डिवाइस को, दिए गए वेंडर (vendorId) और विकल्प के तौर पर प्रॉडक्ट (productId) आइडेंटिफ़ायर से मैच करने के लिए किया जाता है. classCode, protocolCode, serialNumber, और subclassCode कुंजियों को यहां भी बताया जा सकता है.

Chrome में यूएसबी डिवाइस के उपयोगकर्ता के अनुरोध का स्क्रीनशॉट
यूएसबी डिवाइस के उपयोगकर्ता का अनुरोध.

उदाहरण के लिए, ऑरिजिन को अनुमति देने के लिए कॉन्फ़िगर किए गए कनेक्ट किए गए 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 हेक्साडेसिमल नंबर नहीं मिला था. मैंने बस USB आईडी की सूची में "Arduino" शब्द की खोज की है.

ऊपर बताए गए प्रॉमिस में बताए गए यूएसबी device में, डिवाइस के बारे में कुछ बुनियादी और ज़रूरी जानकारी दी गई है. जैसे, डिवाइस के साथ काम करने वाला यूएसबी वर्शन, ज़्यादा से ज़्यादा पैकेट साइज़, वेंडर, और प्रॉडक्ट आईडी. साथ ही, डिवाइस में कितने कॉन्फ़िगरेशन हो सकते हैं. आम तौर पर, इसमें डिवाइस के यूएसबी डिस्क्रिप्टर के सभी फ़ील्ड होते हैं.

// 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"
  });
})

वैसे, अगर कोई यूएसबी डिवाइस WebUSB के साथ काम करने के साथ-साथ लैंडिंग पेज यूआरएल तय करेगा, तो उसके प्लग इन किए जाने पर Chrome, लगातार एक सूचना दिखाएगा. इस सूचना पर क्लिक करने से लैंडिंग पेज खुलेगा.

Chrome में WebUSB की सूचना का स्क्रीनशॉट
WebUSB की सूचना.

Arduino यूएसबी बोर्ड से बात करें

ठीक है, अब देखते हैं कि यूएसबी पोर्ट पर, WebUSB के साथ काम करने वाले Arduino बोर्ड से बातचीत करना कितना आसान है. अपने स्केच को WebUSB-चालू करने के लिए, https://github.com/webusb/arduino पेज पर दिए गए निर्देश देखें.

चिंता न करें, मैं इस लेख में आगे नीचे बताए गए 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 लाइब्रेरी का इस्तेमाल कर रही हूँ वह बस एक उदाहरण प्रोटोकॉल (स्टैंडर्ड यूएसबी सीरियल प्रोटोकॉल पर आधारित) लागू कर रही है. मैन्युफ़ैक्चरर अपनी पसंद के हिसाब से कोई भी सेट और टाइप के एंडपॉइंट बना सकते हैं. छोटे कॉन्फ़िगरेशन कमांड के लिए कंट्रोल ट्रांसफ़र खास तौर पर अच्छा होता है, क्योंकि उनमें बस को प्राथमिकता दी जाती है और इसका स्ट्रक्चर अच्छी तरह से तय होता है.

यह रहा एक स्केच, जिसे 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 सीरियल एपीआई दिखाता है. इसका इस्तेमाल डिफ़ॉल्ट एपीआई को बदलने के लिए किया जा सकता है.

JavaScript कोड को फिर से देखें. उपयोगकर्ता का device चुनने के बाद, device.open() यूएसबी डिवाइस पर सेशन शुरू करने के लिए प्लैटफ़ॉर्म के सभी चरणों को पूरा करता है. इसके बाद, मुझे device.selectConfiguration() के साथ उपलब्ध यूएसबी कॉन्फ़िगरेशन चुनना होगा. याद रखें कि कॉन्फ़िगरेशन से पता चलता है कि डिवाइस कैसे काम करता है, इसकी सबसे ज़्यादा बैटरी कैसे इस्तेमाल की जाती है, और इसके इंटरफ़ेस की संख्या क्या है. इंटरफ़ेस की बात करें, तो मुझे device.claimInterface() के साथ खास ऐक्सेस का अनुरोध भी करना होगा. इसकी वजह यह है कि डेटा को इंटरफ़ेस या उससे जुड़े एंडपॉइंट पर सिर्फ़ तब ट्रांसफ़र किया जा सकता है, जब इंटरफ़ेस पर दावा किया गया हो. आखिर में, device.controlTransferOut() को कॉल करने की ज़रूरत है, ताकि Aduino डिवाइस सेट अप किया जा सके और WebUSB Serial API के ज़रिए संपर्क करने के लिए, सही निर्देश दिए जा सकें.

वहां से, device.transferIn() को डिवाइस पर एक साथ कई फ़ाइलें ट्रांसफ़र की जाती हैं. इससे डिवाइस को यह जानकारी मिलती है कि होस्ट, बल्क डेटा पाने के लिए तैयार है. इसके बाद, प्रॉमिस, DataView data वाले result ऑब्जेक्ट से पूरा किया जाता है. इस ऑब्जेक्ट को सही तरीके से पार्स करना होता है.

अगर आप यूएसबी के बारे में जानते हैं, तो यह सब जाना-पहचाना लग सकता है.

मुझे और चाहिए

WebUSB API की मदद से, सभी तरह के यूएसबी ट्रांसफ़र/एंडपॉइंट से इंटरैक्ट किया जा सकता है:

  • CONTROL ट्रांसफ़र का इस्तेमाल किसी यूएसबी डिवाइस पर कॉन्फ़िगरेशन या कमांड पैरामीटर भेजने या पाने के लिए किया जाता है. इन्हें controlTransferIn(setup, length) और controlTransferOut(setup, data) के साथ हैंडल किया जाता है.
  • रुकावट डालने वाले ट्रांसफ़र का इस्तेमाल कुछ समय के लिए संवेदनशील डेटा के लिए किया जाता है. इन्हें transferIn(endpointNumber, length) और transferOut(endpointNumber, data) के साथ BULK ट्रांसफ़र के साथ-साथ ही हैंडल किया जाता है.
  • ISOCHRONOUS ट्रांसफ़र का इस्तेमाल वीडियो और साउंड जैसे डेटा स्ट्रीम के लिए किया जाता है. इन्हें isochronousTransferIn(endpointNumber, packetLengths) और isochronousTransferOut(endpointNumber, data, packetLengths) के साथ मैनेज किया जाता है.
  • BULK ट्रांसफ़र का इस्तेमाल, बड़ी संख्या में नॉन-टाइम-सेंसिटिव (ज़रूरी नहीं) डेटा को भरोसेमंद तरीके से ट्रांसफ़र करने के लिए किया जाता है. इन्हें transferIn(endpointNumber, length) और transferOut(endpointNumber, data) की मदद से मैनेज किया जाता है.

साथ ही, माइक साओ का WebLight प्रोजेक्ट भी देखा जा सकता है, जिसमें WebUSB API के लिए डिज़ाइन किया गया यूएसबी से कंट्रोल किया जाने वाला एलईडी डिवाइस बनाने का उदाहरण दिया गया है (यहां Arduino का इस्तेमाल नहीं किया जा सकता). यहां आपको हार्डवेयर, सॉफ़्टवेयर, और फ़र्मवेयर मिलेंगे.

यूएसबी डिवाइस का ऐक्सेस वापस लेना

वेबसाइट, USBDevice इंस्टेंस पर forget() को कॉल करके, उस यूएसबी डिवाइस को ऐक्सेस करने की अनुमतियां हटा सकती है जिसकी अब ज़रूरत नहीं है. उदाहरण के लिए, किसी शेयर किए गए कंप्यूटर पर कई डिवाइसों पर इस्तेमाल किया जाने वाला शिक्षा से जुड़ा वेब ऐप्लिकेशन होने पर, यूज़र की जनरेट की गई बड़ी संख्या में अनुमतियों की वजह से उपयोगकर्ता अनुभव खराब होता है.

// Voluntarily revoke access to this USB device.
await device.forget();

Chrome 101 या इसके बाद के वर्शन में forget() उपलब्ध है. इसलिए, देखें कि यह सुविधा इनके साथ काम करती है या नहीं:

if ("usb" in navigator && "forget" in USBDevice.prototype) {
  // forget() is supported.
}

ट्रांसफ़र के साइज़ की सीमाएं

कुछ ऑपरेटिंग सिस्टम इस बात पर सीमाएं लागू करते हैं कि कितना डेटा, मंज़ूरी बाकी वाले यूएसबी लेन-देन का हिस्सा हो सकता है. डेटा को छोटे-छोटे ट्रांज़ैक्शन में बांटने और एक बार में सिर्फ़ कुछ डेटा सबमिट करने से, इन सीमाओं से बचा जा सकता है. इससे इस्तेमाल की जाने वाली मेमोरी भी कम हो जाती है और आपका ऐप्लिकेशन, ट्रांसफ़र पूरा होने पर उसकी प्रोग्रेस को रिपोर्ट कर पाता है.

एंडपॉइंट पर सबमिट किए गए कई ट्रांसफ़र हमेशा क्रम में लागू होते हैं. इसलिए, यूएसबी ट्रांसफ़र के बीच इंतज़ार के समय को रोकने के लिए, सूची में कई हिस्सों को सबमिट करके थ्रूपुट को बेहतर बनाया जा सकता है. जब भी किसी हिस्से को पूरी तरह से ट्रांसमिट किया जाता है, तो वह आपके कोड को यह सूचना देगा कि उसे नीचे दिए गए हेल्पर फ़ंक्शन के उदाहरण में, ज़्यादा डेटा उपलब्ध कराना चाहिए.

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);
}

सलाह

Chrome में यूएसबी को डीबग करने के लिए, अंदरूनी पेज about://device-log पर जाएं. यहां आपको यूएसबी डिवाइस से जुड़े सभी इवेंट एक ही जगह पर दिखेंगे.

Chrome में WebUSB को डीबग करने के लिए, डिवाइस के लॉग पेज का स्क्रीनशॉट
Chrome में डिवाइस लॉग पेज, WebUSB API को डीबग करने के लिए.

अंदरूनी पेज about://usb-internals भी आसानी से इस्तेमाल किया जा सकता है. इसकी मदद से, वर्चुअल USB डिवाइसों के कनेक्शन और डिस्कनेक्शन को सिम्युलेट किया जा सकता है. यह असली हार्डवेयर के बिना, यूज़र इंटरफ़ेस (यूआई) टेस्टिंग करने के लिए फ़ायदेमंद है.

Chrome में WebUSB को डीबग करने के लिए, अंदरूनी पेज का स्क्रीनशॉट
WebUSB API को डीबग करने के लिए, Chrome में अंदरूनी पेज.

ज़्यादातर Linux सिस्टम पर, यूएसबी डिवाइसों को डिफ़ॉल्ट रूप से सिर्फ़ पढ़ने की अनुमति के साथ मैप किया जाता है. Chrome को यूएसबी डिवाइस खोलने की अनुमति देने के लिए, आपको एक नया udev नियम जोड़ना होगा. /etc/udev/rules.d/50-yourdevicename.rules पर, नीचे दिए गए कॉन्टेंट वाली फ़ाइल बनाएं:

SUBSYSTEM=="usb", ATTR{idVendor}=="[yourdevicevendor]", MODE="0664", GROUP="plugdev"

जहां [yourdevicevendor], 2341 है. उदाहरण के लिए, अगर आपका डिवाइस Arduino है, तो. ज़्यादा खास नियम के लिए, ATTR{idProduct} को भी जोड़ा जा सकता है. पक्का करें कि आपका user, plugdev ग्रुप का सदस्य हो. इसके बाद, डिवाइस को फिर से कनेक्ट करें.

रिसॉर्स

हैशटैग #WebUSB इस्तेमाल करके @ChromiumDev को एक ट्वीट भेजें और हमें बताएं कि उसका इस्तेमाल कहां और कैसे किया जा रहा है.

स्वीकार हैं

इस लेख को पढ़ने के लिए, जो मेडली का धन्यवाद.