ウェブ上の USB デバイスへのアクセス

WebUSB API は、USB をウェブに提供することで、USB をより安全かつ使いやすくします。

François Beaufort
François Beaufort

私が率直に言って「USB」と言えば、 すぐに思い浮かぶのはキーボード、マウス、オーディオ、ビデオ、ストレージ デバイスです。あなたは それ以外のタイプのユニバーサル シリアル バス(USB)デバイスは できます。

標準化されていない USB デバイスの場合、ハードウェア ベンダーはプラットフォーム固有の ドライバと SDK をダウンロードしておく必要があります。 残念なことに、これまでは、このプラットフォーム固有のコードによってこれらのデバイスの使用が妨げられていました。 表示されます。これが WebUSB API が作成された理由の一つです。 USB デバイス サービスをウェブに公開する方法を提供します。この API を使用すると クロスプラットフォーム JavaScript SDK を構築して、 できます。

最も重要なのは、今回の変更により、 ウェブに公開します

WebUSB API で想定される動作を見てみましょう。

  1. USB デバイスを購入します。
  2. パソコンに差し込みます。通知がすぐに表示されます。 このデバイスでのウェブサイトを訪問します
  3. この通知をクリックすると、ウェブサイトが完成し、使用できるようになりました。
  4. クリックして接続すると、Chrome に USB デバイス選択ツールが表示され、 デバイスを選択します。

タダ!

WebUSB API を使用しないと、この手順はどのようになりますか。

  1. プラットフォーム固有のアプリケーションをインストールします。
  2. 使用しているオペレーティング システムでもサポートされている場合は、 考えてみましょう
  3. 取り付けます。運が良ければ、恐ろしい OS プロンプトやポップアップは表示されません インターネットからのドライバやアプリケーションのインストールに関する警告。条件 不幸なことに、インストールされているドライバーやアプリケーションの誤動作や損害 ダウンロードします。(ウェブは誤作動を封じ込めることを目的に作られていることに注意してください) ウェブサイトをご覧ください)。
  4. この機能を 1 回だけ使用すると、コードは次の設定を行うまでパソコンに保存されます。 考えてみましょう。(ウェブ上では、未使用の容量は、 reclaimed.)

始める前に

この記事は、USB の仕組みに関する基本的な知識があることを前提としています。そうでない場合 NutShell 内の USB を読むことをおすすめします。USB の背景情報については、 公式の USB 仕様をご確認ください。

WebUSB API を使用可能にしました(Chrome 61)。

オリジン トライアルで利用可能

WebUSB を使用しているデベロッパーからできるだけ多くのフィードバックを この機能は、Chrome 54 と Chrome ですでに追加されています。 57(オリジン トライアル

最新のトライアルは、2017 年 9 月に終了しました。

プライバシーとセキュリティ

HTTPS のみ

この機能は、安全なコンテキストでのみ動作します。つまり TLS を念頭に置いて構築する必要があります。

ユーザー操作が必要です

セキュリティ対策として、navigator.usb.requestDevice() は タップやマウスクリックなどのユーザー操作で呼び出すことができます。

権限に関するポリシー

権限ポリシーは、デベロッパーが ブラウザの各種機能や API を無効にできます。これは HTTP(S) と ヘッダーまたは iframe の「allow」属性です。

usb 属性を付与するかどうかを制御する権限ポリシーを定義できます。 つまり、WebUSB を許可していれば、このオブジェクトは Navigator オブジェクトで公開できます。

WebUSB が許可されていないヘッダー ポリシーの例を次に示します。

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

USB が許可されているコンテナ ポリシーのもう一つの例を以下に示します。

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

コーディングを始めましょう

WebUSB API は、JavaScript の Promise に大きく依存しています。これらのツールに Promise のチュートリアルをご覧ください。終わりに、() => {} さん 単純な ECMAScript 2015 アロー関数です。

USB デバイスへのアクセス権を取得する

navigator.usb.requestDevice() または navigator.usb.getDevices() を呼び出すと、 ウェブサイトによるアクセス権が付与されている、接続されているすべての USB デバイスのリスト。

navigator.usb.requestDevice() 関数は、必須の JavaScript オブジェクトを受け取ります。 filters を定義します。これらのフィルタは、該当する USB デバイスの 指定されたベンダー(vendorId)と商品(productId)の ID(省略可)。 classCodeprotocolCodeserialNumbersubclassCode キーでは、 定義します。

<ph type="x-smartling-placeholder">
</ph> Chrome に表示された USB デバイスのユーザー プロンプトのスクリーンショット
USB デバイスのユーザー プロンプト

たとえば、接続されている 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 の 16 進数は魔法のように思いつきませんでした あります。「Arduino」で検索してみました(USB ID のリストを参照)

上記のフルフィルメントの Promise で返される USB device には、依然として基本的なものがいくつかあります。 サポートされている USB バージョン、 最大パケットサイズ、ベンダー、プロダクト ID、 構成を設定できます。基本的に、このファイルには device 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 のサポートを発表しました。 Chrome でランディング ページ URL を定義している場合、 USB デバイスが接続されています。この通知をクリックすると、ランディング ページが開きます。

<ph type="x-smartling-placeholder">
</ph> Chrome の WebUSB 通知のスクリーンショット
WebUSB 通知

Arduino USB ボードと通信する

では、さっそく 1 対 1 の WebB 互換の USB ポートと USB ポート上の Arduino ボード。手順についてはこちらをご覧ください: 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 ライブラリは、 (標準の USB シリアル プロトコルに基づく)サンプル プロトコルを 1 つ作成し、 任意のセットとタイプのエンドポイントを作成できます。 制御転送は、次のような小規模な構成コマンドに適しています。 バスの優先度が設定され、構造が明確に定義されています。

これが 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 つを行います。

  • このデバイスは、Chrome でランディング ページ URL を読み取るための WebUSB デバイスとして機能します。
  • 公開する WebUSB Serial API を使用して、デフォルトの API をオーバーライドできます。

JavaScript コードをもう一度見てみましょう。ユーザーが device を選択したら、 device.open() は、プラットフォーム固有の手順をすべて実行して、USB によるセッションを開始します。 ダウンロードします次に、使用可能な USB 構成を選択する必要があります。 device.selectConfiguration()。設定では、インフラストラクチャの 電源が入っているかどうか、最大消費電力、インターフェース数など)を確認します。 インターフェースについても、API による排他アクセスをリクエスト device.claimInterface(): データはインターフェースまたはデータのみが転送されるため、 エンドポイントが関連付けられます。最後に Arduino デバイスをセットアップするには、device.controlTransferOut() が必要です。 適切なコマンドを使用して通信する必要があります。

その後、device.transferIn() が転送を一括転送して、 ホストが一括データを受信する準備ができたことを通知します。次に、 Promise は、DataView data を含む result オブジェクトで履行されます。 適切に解析する必要があります

USB を使い慣れている方なら、これらすべてに見覚えがあるでしょう。

もっと必要です

WebUSB API を使用すると、あらゆるタイプの USB 転送/エンドポイントを操作できます。

  • 設定またはコマンドの送受信に使用される CONTROL 転送 パラメータは、controlTransferIn(setup, length)controlTransferOut(setup, data) で処理されます。
  • 割り込み転送は、短時間にセンシティブ データに使用されるが、 一括転送と同じ方法で処理できます。 transferIn(endpointNumber, length)transferOut(endpointNumber, data)
  • 動画や音声などのデータ ストリームに使用される ISOCHRONOUS 転送は、 isochronousTransferIn(endpointNumber, packetLengths) で処理し、 isochronousTransferOut(endpointNumber, data, packetLengths)
  • 一括転送。時間的制約のない大量のデータを転送するために使用します。 transferIn(endpointNumber, length) で処理されます。 transferOut(endpointNumber, data)

また、Mike Tsao の WebLight プロジェクトも参考にしてください。 は、USB で制御される LED デバイスを開発するためのゼロタッチの 使用します(ここでは Arduino を使用しません)。ハードウェア、ソフトウェア ソフトウェアです。

USB デバイスへのアクセス権を取り消す

ウェブサイトで不要になった USB デバイスにアクセスするための権限をクリーンアップする 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);
}

ヒント

内部ページ about://device-log を使用すると、Chrome での USB デバッグが簡単になります USB デバイスに関連するすべてのイベントを 1 か所で確認できます。

<ph type="x-smartling-placeholder">
</ph> Chrome で WebUSB をデバッグするためのデバイスログページのスクリーンショット
WebUSB API をデバッグするための Chrome のデバイスログ ページ

内部ページ about://usb-internals も便利で、次のことができます。 仮想 WebUSB デバイスの接続と切断をシミュレートします。 これは、実際のハードウェアを使用せずに UI テストを行う場合に便利です。

<ph type="x-smartling-placeholder">
</ph> Chrome で WebUSB をデバッグするための社内ページのスクリーンショット
WebUSB API をデバッグするための Chrome の社内ページ

ほとんどの Linux システムでは、USB デバイスは あります。Chrome で USB デバイスを開けるようにするには、新しい udev ファイルを追加する必要があります。 ルールをご覧ください。/etc/udev/rules.d/50-yourdevicename.rules にファイルを 次の内容が含まれます。

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

たとえば、デバイスが Arduino の場合は、[yourdevicevendor]2341 です。 より具体的なルールが必要な場合は、ATTR{idProduct} を追加します。必ず userplugdev グループのメンバーです。その後、デバイスを再接続してください。

リソース

ハッシュタグを使用して @ChromiumDev にツイートしてください #WebUSB どこで、どのように使用されているかをお知らせください。

謝辞

この記事をレビューしてくれた Joe Medley に感謝します。