웹에서 USB 기기에 액세스

WebUSB API를 사용하면 USB를 웹으로 가져와 USB를 더 안전하고 쉽게 사용할 수 있습니다.

François Beaufort
François Beaufort

명료하게 "USB"라고만 할 경우, 아마도 USB는 키보드, 마우스, 오디오, 비디오 및 저장 장치를 즉시 떠올릴 수 있습니다. 여러분은 하지만 다른 종류의 범용 직렬 버스 (USB) 장치는 있습니다.

이러한 표준화되지 않은 USB 기기는 하드웨어 공급업체가 플랫폼별로 작성해야 합니다. 이를 활용할 수 있도록 여러분 (개발자)이 활용할 수 있도록 해야 합니다. 안타깝게도 이 플랫폼별 코드로 인해 지금까지 이러한 기기의 사용이 차단됐습니다. 있습니다. 이것이 WebUSB API가 만들어진 이유 중 하나입니다. USB 기기 서비스를 웹에 노출하는 방법을 제공합니다. 이 API를 사용하면 제조업체는 자체 플랫폼을 위한 크로스 플랫폼 JavaScript SDK를 기기에서 사용할 수 있습니다.

하지만 가장 중요한 것은 이를 통해 USB를 더욱 안전하고 쉽게 사용할 수 있도록 해 주므로 웹에 업로드할 수 있습니다.

WebUSB API에서 예상되는 동작을 살펴보겠습니다.

  1. USB 기기를 구매하세요.
  2. 컴퓨터에 연결합니다. 알림은 알림의 오른쪽과 함께 이동해야 합니다.
  3. 알림을 클릭합니다. 웹사이트가 표시되고 사용할 준비가 되었습니다.
  4. 클릭하여 연결하면 Chrome에 USB 기기 선택기가 표시되며 여기에서 기기를 선택하세요.

짜잔!

WebUSB API가 없으면 이 절차는 어떻게 되나요?

  1. 플랫폼별 애플리케이션을 설치합니다.
  2. 내 운영 체제에서 지원되는 경우 생각해야 합니다.
  3. 설치 운이 좋으면 무서운 OS 메시지나 팝업이 표시되지 않습니다. 인터넷상의 드라이버/애플리케이션 설치에 대해 경고하는 메시지도 표시됩니다. 만약 설치된 드라이버나 애플리케이션이 오작동하거나 해를 입는 경우 설치하시기 바랍니다. (웹은 오작동하는 컴퓨터 문제를 감지하도록 만들어져 있습니다.) 웹사이트)에서 사용할 수 있습니다.
  4. 이 기능을 한 번만 사용하면 이 코드는 그것을 제거하십시오. (웹에서는 결국 사용되지 않는 공간은 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를 허용하는 경우에만 노출됩니다.

다음은 WebUSB가 허용되지 않는 헤더 정책의 예입니다.

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

다음은 USB가 허용되는 컨테이너 정책의 또 다른 예입니다.

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

코딩을 시작해 볼까요?

WebUSB API는 JavaScript 프로미스에 크게 의존합니다. 이 프로미스 튜토리얼을 확인해 보세요. 한 가지 더 알려드릴 사항이 있습니다. () => {} 단순히 ECMAScript 2015 화살표 함수입니다.

USB 기기에 액세스

다음 중 하나를 사용하여 사용자에게 연결된 단일 USB 기기를 선택하라는 메시지를 표시할 수 있습니다. navigator.usb.requestDevice() 또는 navigator.usb.getDevices()번으로 전화하여 웹사이트에 액세스 권한이 부여된 연결된 모든 USB 기기의 목록입니다.

navigator.usb.requestDevice() 함수는 필수 JavaScript 객체를 사용합니다. - filters를 정의하는 이 필터는 모든 USB 장치를 제공된 공급업체(vendorId) 및 제품(productId) 식별자(선택사항) classCode, protocolCode, serialNumber, subclassCode 키는 다음을 수행할 수 있습니다. 거기에도 정의되어 있습니다

<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 목록에서 확인할 수 있습니다.

위의 처리된 프로미스에서 반환된 USB device에는 기본적이지만 지원되는 USB 버전, 최대 패킷 크기, 공급업체 및 제품 ID, 구성 설정이 있을 수 있습니다. 기본적으로 이 파일에는 기기 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을 정의하면 USB 기기가 연결되어 있습니다. 이 알림을 클릭하면 방문 페이지가 열립니다.

<ph type="x-smartling-placeholder">
</ph> Chrome의 WebUSB 알림 스크린샷
WebUSB 알림.

Arduino USB 보드와 통신

자, 이제 WebUSB 호환 장치에서 얼마나 쉽게 통신하는지 살펴보겠습니다. 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 직렬 프로토콜 기반)과 제조업체는 원하는 엔드포인트 유형 및 세트를 생성할 수 있습니다. 제어 전송은 작은 구성 명령에 특히 적합하기 때문에 버스 우선순위를 얻고 잘 정의된 구조를 가집니다.

이것은 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 라이브러리는 다음을 수행합니다. 기본적으로 두 가지입니다.

  • 이 기기는 Chrome에서 방문 페이지 URL을 읽을 수 있도록 하는 WebUSB 기기 역할을 합니다.
  • 기본 API를 재정의하는 데 사용할 수 있는 WebUSB Serial API를 노출합니다.

JavaScript 코드를 다시 확인합니다. 사용자가 device를 선택하면 device.open()는 모든 플랫폼별 단계를 실행하여 USB로 세션을 시작합니다. 있습니다. 그런 다음에는 포트 80에서 사용 가능한 USB 구성을 device.selectConfiguration() 구성은 장치의 전원, 최대 전력 소모량 및 인터페이스 수를 나타냅니다. 인터페이스에 관해 설명하자면, device.claimInterface() - 데이터는 인터페이스 또는 인터페이스가 클레임되면 연결된 엔드포인트를 반환합니다. 최종 통화 device.controlTransferOut()는 Arduino 기기를 적절한 명령을 사용하여 WebUSB Serial API를 통해 통신합니다.

여기에서 device.transferIn()는 장치를 사용하여 호스트가 대량 데이터를 수신할 준비가 되었음을 알립니다. 그런 다음 프로미스는 DataView data가 포함된 result 객체로 처리됩니다. 적절하게 파싱해야 합니다.

USB에 익숙하다면 이 모든 것이 꽤 익숙할 것입니다.

더 많은 것을 원함

WebUSB API를 사용하면 모든 USB 전송/엔드포인트 유형과 상호작용할 수 있습니다.

  • 구성 또는 명령을 전송 또는 수신하는 데 사용되는 전송 제어 매개변수는 controlTransferIn(setup, length)controlTransferOut(setup, data)로 처리됩니다.
  • 짧은 시간에 민감한 데이터에 사용되는 INTERRUPT 전송은 를 사용한 BULK 전송과 동일한 방법으로 처리됩니다. 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 기기 관련 이벤트를 한곳에서 확인할 수 있습니다.

<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}를 추가할 수도 있습니다. 먼저 user님은 plugdev 그룹의 구성원입니다. 그런 다음 기기를 다시 연결하면 됩니다.

리소스

해시태그를 사용하여 @ChromiumDev에 트윗을 보냅니다. #WebUSB 어디서 어떻게 사용하는지 Google에 알려주세요.

감사의 말씀

이 기사를 검토해 주신 Joe Medley에게 감사드립니다.