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

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. अगर इस सुविधा का इस्तेमाल सिर्फ़ एक बार किया जाता है, तो कोड आपके कंप्यूटर पर तब तक बना रहता है, जब तक आप उसे हटाने का फ़ैसला नहीं ले लेते. (वेब पर, इस्तेमाल न किए गए स्पेस को आखिर में फिर से हासिल कर लिया जाता है.)

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

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

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

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

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

नया ट्रायल, सितंबर 2017 में खत्म हो गया है.

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

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

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

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

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

अनुमतियों की नीति

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

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

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

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

मुझे और जानकारी चाहिए

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

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

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

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

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

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

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

सलाह

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

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

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

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

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

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

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

संसाधन

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

धन्यवाद

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