Web'deki USB Cihazlara Erişme

WebUSB API, USB'yi web'e taşıyarak daha güvenli ve kullanımı kolay hale getirir.

François Beaufort
François Beaufort

"USB" dediğimde aklınıza hemen klavye, fare, ses, video ve depolama cihazları gelebilir. Doğru, ancak başka tür Evrensel Seri Yol (USB) cihazları da vardır.

Standart olmayan bu USB cihazlardan yararlanabilmeniz için (geliştirici) donanım tedarikçilerinin platforma özel sürücüler ve SDK'lar yazması gerekir. Maalesef bu platforma özel kod, geçmişte bu cihazların web tarafından kullanılmasını engelliyordu. WebUSB API'nin oluşturulma nedenlerinden biri de USB cihaz hizmetlerini Web'e göstermenin bir yolunu sağlamaktır. Donanım üreticileri bu API ile cihazları için platformlar arası JavaScript SDK'ları oluşturabilir.

Ancak en önemlisi, bu sayede USB'yi web'e taşıyarak daha güvenli ve kolay bir şekilde kullanılmasını sağlayabilirsiniz.

WebUSB API ile bekleyebileceğiniz davranışı inceleyelim:

  1. USB cihaz satın alın.
  2. Ekranı bilgisayarınıza takın. Bu cihaz için gidilecek doğru web sitesinin bulunduğu bir bildirim hemen gösterilir.
  3. Bildirimi tıklayın. Web sitesi kullanıma hazır.
  4. Bağlantı simgesini tıkladığınızda Chrome'da cihazınızı seçebileceğiniz bir USB cihaz seçici gösterilir.

İşte bu kadar.

WebUSB API olmadan bu prosedür nasıl olur?

  1. Platforma özel bir uygulama yükleyin.
  2. Bu özellik işletim sistemimde destekleniyorsa doğru şeyi indirdiğimden emin olun.
  3. Cihazı kurun. Şanslıysanız sizi internetten sürücü/uygulama yükleme konusunda uyaran korkutucu işletim sistemi istemleri veya pop-up'lar görmezsiniz. Şanssızsanız yüklü sürücüler veya uygulamalar arızalanır ve bilgisayarınıza zarar verir. (Web'in düzgün çalışmayan web siteleri içerecek şekilde tasarlandığını unutmayın).
  4. Bu özelliği yalnızca bir kez kullanırsanız siz onu kaldırmayı düşünene kadar kod bilgisayarınızda kalır. (Web'de, kullanılmayan alan sonunda yeniden alınır.)

Başlamadan önce

Bu makalede, USB'nin işleyiş şekliyle ilgili temel düzeyde bilgi sahibi olduğunuz varsayılmaktadır. Aksi takdirde USB in a NutShell (USB'yi Anlama Kılavuzu) başlıklı makaleyi okumanızı öneririz. USB hakkında arka plan bilgisi için resmi USB özelliklerine bakın.

WebUSB API, Chrome 61'de kullanılabilir.

Kaynak denemelerinde kullanılabilir

Sahada WebUSB API kullanan geliştiricilerden mümkün olduğunca fazla geri bildirim almak için bu özelliği daha önce Chrome 54 ve Chrome 57'ye kaynak deneme sürümü olarak eklemiştik.

Son deneme sürümü Eylül 2017'de başarıyla sona erdi.

Gizlilik ve güvenlik

Yalnızca HTTPS

Bu özellik, gücü sayesinde yalnızca güvenli bağlamlarda çalışır. Bu nedenle, TLS'yi göz önünde bulundurarak uygulamanızı oluşturmanız gerekir.

Kullanıcı hareketi gerekiyor

Bir güvenlik önlemi olarak navigator.usb.requestDevice(), yalnızca dokunma veya fare tıklaması gibi bir kullanıcı hareketiyle çağrılabilir.

İzin Politikası

İzin Politikası, geliştiricilerin çeşitli tarayıcı özelliklerini ve API'lerini seçerek etkinleştirmesine ve devre dışı bırakmasına olanak tanıyan bir mekanizmadır. Bir HTTP başlığı ve/veya iframe "allow" özelliği aracılığıyla tanımlanabilir.

usb özelliğinin Navigator nesnesinde gösterilip gösterilmeyeceğini veya başka bir deyişle WebUSB'ye izin verip vermeyeceğinizi kontrol eden bir izin politikası tanımlayabilirsiniz.

Aşağıda, WebUSB'ye izin verilmeyen bir başlık politikası örneği verilmiştir:

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

Aşağıda, USB'ye izin verilen başka bir kapsayıcı politikası örneği verilmiştir:

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

Kodlamaya başlayalım

WebUSB API, yoğun olarak JavaScript Promises'i kullanır. Sözler hakkında bilgi edinmek için bu mükemmel Sözler eğitim videosuna göz atın. Bir de () => {} basitçe ECMAScript 2015 ok işlevleridir.

USB cihazlara erişim

navigator.usb.requestDevice() kullanarak kullanıcıdan tek bir bağlı USB cihazı seçmesini isteyebilir veya web sitesinin erişimine izin verilen tüm bağlı USB cihazlarının listesini almak için navigator.usb.getDevices() işlevini çağırabilirsiniz.

navigator.usb.requestDevice() işlevi, filters değerini tanımlayan zorunlu bir JavaScript nesnesi alır. Bu filtreler, herhangi bir USB cihazını verilen tedarikçi (vendorId) ve isteğe bağlı olarak ürün (productId) tanımlayıcılarıyla eşleştirmek için kullanılır. classCode, protocolCode, serialNumber ve subclassCode anahtarları da burada tanımlanabilir.

Chrome&#39;daki USB cihaz kullanıcı istemi ekran görüntüsü
USB cihaz kullanıcı istemi.

Örneğin, kaynağa izin verecek şekilde yapılandırılmış bağlı bir Arduino cihazına nasıl erişeceğiniz aşağıda açıklanmıştır.

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

Sormadan önce, bu 0x2341 on altılık sayıyı sihirli bir şekilde bulmadığımı belirtmek isterim. Bu USB kimlikleri listesinde "Arduino" kelimesini aradım.

Yukarıdaki sözü yerine getiren USB device, cihazla ilgili desteklenen USB sürümü, maksimum paket boyutu, tedarikçi firma, ürün kimlikleri ve cihazın sahip olabileceği yapılandırmaların sayısı gibi bazı temel ancak önemli bilgileri içerir. Temelde cihaz USB Tanımlayıcısı'ndaki tüm alanları içerir.

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

Bir USB cihazı, WebUSB desteğini duyurmanın yanı sıra bir açılış sayfası URL'si tanımlarsa Chrome, USB cihaz takıldığında kalıcı bir bildirim gösterir. Bu bildirimi tıkladığınızda açılış sayfası açılır.

Chrome&#39;daki WebUSB bildiriminin ekran görüntüsü
WebUSB bildirimi.

Arduino USB kartıyla konuşma

Şimdi, WebUSB uyumlu bir Arduino kartından USB bağlantı noktası üzerinden iletişim kurmanın ne kadar kolay olduğuna bakalım. Eskizlerinizi WebUSB'de etkinleştirmek için https://github.com/webusb/arduino adresindeki talimatları inceleyin.

Endişelenmeyin, aşağıda bahsedilen tüm WebUSB cihaz yöntemlerini bu makalenin ilerleyen bölümlerinde ele alacağız.

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

Kullandığım WebUSB kitaplığının yalnızca bir örnek protokolü (standart USB seri protokolüne dayalı) uyguladığını ve üreticilerin istedikleri uç nokta grubunu ve türlerini oluşturabileceğini unutmayın. Kontrol aktarımları, veri yolu önceliği olduğundan ve iyi tanımlanmış bir yapıya sahip olduğundan özellikle küçük yapılandırma komutları için kullanışlıdır.

Arduino kartına yüklenen taslak da burada gösterilmektedir.

// 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.
}

Yukarıdaki örnek kodda kullanılan üçüncü taraf WebUSB Arduino kitaplığı temel olarak iki şey yapar:

  • Cihaz, Chrome'un açılış sayfası URL'sini okumasını sağlayan bir WebUSB cihazı gibi çalışır.
  • Bu işlemle, varsayılan API'yi geçersiz kılmak için kullanabileceğiniz bir WebUSB Serial API gösterilir.

JavaScript koduna tekrar bakın. Kullanıcının seçtiği device alındıktan sonra device.open(), USB cihazıyla oturum başlatmak için platforma özel tüm adımları çalıştırır. Ardından, device.selectConfiguration() ile kullanılabilen bir USB yapılandırması seçmem gerekiyor. Bir yapılandırmada cihazın nasıl güç aldığı, maksimum güç tüketimi ve arayüz sayısı belirtilir. Arayüzden bahsetmişken, veriler yalnızca arayüz için hak talebinde bulunulduğunda bir arayüze veya ilişkili uç noktalara aktarılabileceğinden device.claimInterface() ile özel erişim isteğinde de bulunmam gerekiyor. Son olarak, Arduino cihazını WebUSB Seri API üzerinden iletişim kuracak uygun komutlarla ayarlamak için device.controlTransferOut() çağrısı gerekir.

Ardından device.transferIn(), ana makinenin toplu veri almaya hazır olduğunu bildirmek için cihaza toplu aktarım gerçekleştirir. Ardından, söz, uygun şekilde ayrıştırılması gereken bir DataView data içeren bir result nesnesi ile yerine getirilir.

USB'ye aşinasanız tüm bunlar size tanıdık gelecektir.

Daha fazlasını istiyorum

WebUSB API, tüm USB aktarım/uç nokta türleriyle etkileşim kurmanıza olanak tanır:

  • Bir USB cihaza yapılandırma veya komut parametreleri göndermek ya da almak için kullanılan KONTROL aktarımlarında controlTransferIn(setup, length) ve controlTransferOut(setup, data) kullanılır.
  • Zamana duyarlı küçük miktarda veri için kullanılan KESİNTİ aktarımları, transferIn(endpointNumber, length) ve transferOut(endpointNumber, data) ile BULK aktarımlarıyla aynı yöntemlerle işlenir.
  • Görüntü ve ses gibi veri akışları için kullanılan ISOCHRONOUS aktarımları, isochronousTransferIn(endpointNumber, packetLengths) ve isochronousTransferOut(endpointNumber, data, packetLengths) ile işlenir.
  • Zamana duyarlı olmayan büyük miktarda veriyi güvenilir bir şekilde aktarmak için kullanılan TOP aktarımları transferIn(endpointNumber, length) ve transferOut(endpointNumber, data) ile işlenir.

Ayrıca Mike Tsao'nun WebLight projesine göz atabilirsiniz. Bu proje, WebUSB API için tasarlanmış, USB kontrollü bir LED cihazının (burada Arduino'yu kullanmayan bir cihazın) ilkeli bir örneğini sunar. Donanım, yazılım ve donanım yazılımı bulunur.

USB cihazına erişimi iptal etme

Web sitesi, USBDevice örneğinde forget() çağrısını yaparak artık ihtiyaç duymadığı bir USB cihazına erişim izinlerini temizleyebilir. Örneğin, birçok cihazın bulunduğu paylaşılan bir bilgisayarda kullanılan eğitsel bir web uygulamasında, kullanıcı tarafından oluşturulan çok sayıda izin kötü bir kullanıcı deneyimi oluşturur.

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

forget(), Chrome 101 veya sonraki sürümlerde kullanılabildiğinden bu özelliğin aşağıdakilerle desteklenip desteklenmediğini kontrol edin:

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

Aktarım boyutuyla ilgili sınırlamalar

Bazı işletim sistemleri, bekleyen USB işlemlerinin ne kadar veri içerebileceğine dair sınırlar uygular. Verilerinizi daha küçük işlemlere ayırıp bir seferde yalnızca birkaçını göndermek bu sınırlamaları önlemeye yardımcı olur. Ayrıca, kullanılan bellek miktarını azaltır ve aktarımlar tamamlanırken uygulamanızın ilerlemeyi bildirmesine olanak tanır.

Bir uç noktaya gönderilen birden fazla aktarım her zaman sırayla yürütüldüğünden, USB aktarımları arasında gecikmeyi önlemek için sıraya alınmış birden fazla parça göndererek aktarım hızını artırabilirsiniz. Bir parça tamamen iletildiğinde, kodunuza aşağıdaki yardımcı işlev örneğinde belirtildiği gibi daha fazla veri sağlaması gerektiği bildirilir.

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

İpuçları

USB cihazıyla ilgili tüm etkinlikleri tek bir yerden görebileceğiniz dahili sayfa about://device-log sayesinde Chrome'da USB'de hata ayıklama işlemi daha kolaydır.

Chrome&#39;da WebUSB&#39;de hata ayıklama için cihaz günlük sayfasının ekran görüntüsü
WebUSB API'de hata ayıklama için Chrome'daki cihaz günlük sayfası.

about://usb-internals adlı dahili sayfa da kullanışlıdır ve sanal WebUSB cihazlarının bağlantısını ve bağlantısını kesme işlemlerini simüle etmenize olanak tanır. Bu, gerçek donanım olmadan kullanıcı arayüzü testi yapmak için yararlı olabilir.

Chrome&#39;da WebUSB hatalarını ayıklamak için kullanılan dahili sayfanın ekran görüntüsü
WebUSB API'de hata ayıklama için Chrome'daki dahili sayfa.

Çoğu Linux sisteminde USB cihazlar varsayılan olarak salt okuma izinleriyle eşlenir. Chrome'un USB cihazı açmasına izin vermek için yeni bir udev kuralı eklemeniz gerekir. /etc/udev/rules.d/50-yourdevicename.rules adresinde aşağıdaki içeriğe sahip bir dosya oluşturun:

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

Örneğin, cihazınız Arduino ise [yourdevicevendor], 2341 olur. ATTR{idProduct} daha spesifik bir kural için de eklenebilir. user adlı öğenizin plugdev grubunun üyesi olduğundan emin olun. Ardından cihazınızı yeniden bağlayın.

Kaynaklar

#WebUSB hashtag'ini kullanarak @ChromiumDev hesabına tweet gönderin ve bu özelliği nerede ve nasıl kullandığınızı bize bildirin.

Teşekkür

Bu makaleyi inceleyen Joe Medley'e teşekkürler.