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

WebUSB API, USB को वेब पर उपलब्ध कराकर, इसे ज़्यादा सुरक्षित और इस्तेमाल में आसान बनाता है.

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 Promises पर काफ़ी निर्भर करता है. अगर आपको इनके बारे में जानकारी नहीं है, तो Promises ट्यूटोरियल देखें. एक और बात, () => {} सिर्फ़ 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 Serial API दिखाता है, जिसका इस्तेमाल डिफ़ॉल्ट एपीआई को बदलने के लिए किया जा सकता है.

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

किसी एंडपॉइंट पर सबमिट किए गए कई ट्रांसफ़र हमेशा क्रम में लागू होते हैं. इसलिए, 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);
}

सलाह

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

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 को ट्वीट करें और हमें बताएं कि इसका इस्तेमाल कहां और कैसे किया जा रहा है.

आभार

इस लेख की समीक्षा करने के लिए, Joe Medley का धन्यवाद.