อุปกรณ์ USB

เอกสารนี้จะอธิบายวิธีใช้ USB API เพื่อสื่อสารกับอุปกรณ์ USB อุปกรณ์บางอย่างจะเข้าถึงผ่าน USB API ไม่ได้ (ดูรายละเอียดในส่วนคำเตือนด้านล่าง) แอป Chrome ยังเชื่อมต่อกับอุปกรณ์ serial และบลูทูธได้ด้วย

หากต้องการข้อมูลพื้นฐานเกี่ยวกับ USB โปรดดูข้อมูลจำเพาะของ USB อย่างเป็นทางการ USB ใน NutShell เป็นข้อขัดข้องที่สมเหตุสมผลซึ่งอาจเป็นประโยชน์

ข้อกำหนดเกี่ยวกับไฟล์ Manifest

USB API ต้องใช้สิทธิ์ "usb" ในไฟล์ Manifest ดังนี้

"permissions": [
  "usb"
]

นอกจากนี้ เพื่อป้องกันลายนิ้วมือ คุณต้องประกาศประเภทอุปกรณ์ทั้งหมดที่ต้องการเข้าถึงในไฟล์ Manifest อุปกรณ์ USB แต่ละประเภทจะสอดคล้องกับคู่รหัสผู้ให้บริการ/รหัสผลิตภัณฑ์ (VID/PID) คุณสามารถใช้ usb.getDevices เพื่อแจกแจงอุปกรณ์ตามคู่ VID/PID

คุณต้องประกาศคู่ VID/PID สำหรับอุปกรณ์แต่ละประเภทที่ต้องการใช้ภายใต้สิทธิ์ usbDevices ในไฟล์ Manifest ของแอปดังที่แสดงในตัวอย่างด้านล่าง

"permissions": [
  {
    "usbDevices": [
      {
        "vendorId": 123,
        "productId": 456
      }
    ]
  }
]

ตั้งแต่ Chrome 57 เป็นต้นไป ข้อกำหนดในการประกาศประเภทอุปกรณ์ทั้งหมดในไฟล์ Manifest ของแอปจะได้รับการผ่อนปรนสำหรับแอปที่ทำงานเป็นแอปคีออสก์ของ ChromeOS สำหรับแอปคีออสก์ คุณสามารถใช้พร็อพเพอร์ตี้สิทธิ์ interfaceClass เพื่อขอสิทธิ์เข้าถึงอุปกรณ์ USB ที่มีลักษณะดังนี้

  • ใช้อินเทอร์เฟซ USB ของคลาสอินเทอร์เฟซเฉพาะ
  • มีคลาสอุปกรณ์ USB ที่เจาะจง

ตัวอย่างเช่น สิทธิ์ usbDevices ต่อไปนี้จะอนุญาตให้แอปเข้าถึงอุปกรณ์ USB ทั้งหมดที่ใช้อินเทอร์เฟซของเครื่องพิมพ์ (รหัสคลาสอินเทอร์เฟซ 7) และอุปกรณ์ฮับ USB (รหัสคลาสอุปกรณ์ 9)

"permissions": [
  {
    "usbDevices": [
      {"interfaceClass": 7},
      {"interfaceClass": 9}
    ]
  }
]

ดูรายการค่า interfaceClass ที่ยอมรับได้ที่หัวข้อรหัสคลาส USB

คุณรวมพร็อพเพอร์ตี้ interfaceClass กับพร็อพเพอร์ตี้ vendorId เพื่อรับสิทธิ์เข้าถึงอุปกรณ์ USB จากผู้ให้บริการที่เจาะจงเท่านั้นได้ตามตัวอย่างต่อไปนี้

"permissions": [
  {
    "usbDevices": [
      {
        "vendorId": 123,
        "interfaceClass": 7
      }
    ]
  }
]

กำลังค้นหาอุปกรณ์

หากต้องการระบุว่าอุปกรณ์อย่างน้อย 1 เครื่องเชื่อมต่ออยู่กับระบบของผู้ใช้ ให้ใช้เมธอด usb.getDevices ดังนี้

chrome.usb.getDevices(enumerateDevicesOptions, callback);
พารามิเตอร์ (ประเภท)คำอธิบาย
EnumeratedevicesOptions (ออบเจ็กต์)วัตถุที่ระบุทั้ง vendorId (ยาว) และ productId (ยาว) ที่ใช้ค้นหาประเภทอุปกรณ์ที่ถูกต้องบนรถบัส ไฟล์ Manifest ต้องประกาศส่วนสิทธิ์ usbDevices ที่แสดงคู่ vendorId และ deviceId ทั้งหมดที่แอปของคุณต้องการเข้าถึง
โค้ดเรียกกลับ (ฟังก์ชัน)มีการเรียกเมื่อการแจงนับอุปกรณ์เสร็จสิ้น โค้ดเรียกกลับจะทำงานด้วยพารามิเตอร์ 1 รายการ ซึ่งเป็นอาร์เรย์ของออบเจ็กต์ Device ที่มีพร็อพเพอร์ตี้ 3 รายการ ได้แก่ device, vendorId, productId พร็อพเพอร์ตี้ของอุปกรณ์คือตัวระบุแบบคงที่สำหรับอุปกรณ์ที่เชื่อมต่อ โดยจะไม่เปลี่ยนแปลงจนกว่าจะถอดปลั๊กอุปกรณ์ รายละเอียดของตัวระบุเป็นสีทึบและอาจมีการเปลี่ยนแปลง อย่าพึ่งพาประเภทปัจจุบัน
หากไม่พบอุปกรณ์ อาร์เรย์จะว่างเปล่า

ตัวอย่าง

function onDeviceFound(devices) {
  this.devices=devices;
  if (devices) {
    if (devices.length > 0) {
      console.log("Device(s) found: "+devices.length);
    } else {
      console.log("Device could not be found");
    }
  } else {
    console.log("Permission denied.");
  }
}

chrome.usb.getDevices({"vendorId": vendorId, "productId": productId}, onDeviceFound);

การเปิดอุปกรณ์

เมื่อส่งคืนออบเจ็กต์ Device แล้ว คุณจะเปิดอุปกรณ์ที่ใช้ usb.openDevice เพื่อรับแฮนเดิลการเชื่อมต่อได้ คุณจะสื่อสารกับอุปกรณ์ USB ได้โดยใช้แฮนเดิลการเชื่อมต่อเท่านั้น

พร็อพเพอร์ตี้คำอธิบาย
อุปกรณ์ได้รับออบเจ็กต์ในโค้ดเรียกกลับ usb.getDevices
ข้อมูล (Arraybuffer)มีข้อมูลที่ส่งโดยอุปกรณ์หากการโอนเป็นขาเข้า

ตัวอย่าง

var usbConnection = null;
var onOpenCallback = function(connection) {
  if (connection) {
    usbConnection = connection;
    console.log("Device opened.");
  } else {
    console.log("Device failed to open.");
  }
};

chrome.usb.openDevice(device, onOpenCallback);

เพื่อให้ขั้นตอนการเปิดง่ายขึ้น คุณสามารถใช้เมธอด usb.findDevices ซึ่งแจกแจง ขอสิทธิ์เข้าถึง และเปิดอุปกรณ์ในการเรียกใช้ครั้งเดียว ดังนี้

chrome.usb.findDevices({"vendorId": vendorId, "productId": productId, "interfaceId": interfaceId}, callback);

ซึ่งเทียบเท่ากับ

chrome.usb.getDevices({"vendorId": vendorId, "productId": productId}, function (devices) {
  if (!devices) {
    console.log("Error enumerating devices.");
    callback();
    return;
  }
  var connections = [], pendingAccessRequests = devices.length;
  devices.forEach(function (device) {
    chrome.usb.requestAccess(interfaceId, function () {
      // No need to check for errors at this point.
      // Nothing can be done if an error occurs anyway. You should always try
      // to open the device.
      chrome.usb.openDevices(device, function (connection) {
        if (connection) connections.push(connection);
        pendingAccessRequests--;
        if (pendingAccessRequests == 0) {
          callback(connections);
        }
      });
    });
  })
});

โอนและรับข้อมูลผ่าน USB จากอุปกรณ์

โปรโตคอล USB กำหนดการโอนข้อมูล 4 ประเภท ได้แก่ การควบคุม เป็นกลุ่ม ไม่คงที่ และขัดข้อง รายละเอียดการโอนมีดังนี้

การโอนอาจเกิดขึ้นได้ทั้ง 2 ทาง ได้แก่ ระหว่างอุปกรณ์ต่อโฮสต์ (ขาเข้า) และระหว่างโฮสต์ไปยังอุปกรณ์ (ขาออก) โฮสต์ (คอมพิวเตอร์ที่เรียกใช้แอป Chrome) ต้องเป็นผู้เริ่มต้นทั้งข้อความขาเข้าและขาออกเนื่องจากลักษณะของโปรโตคอล USB สำหรับข้อความขาเข้า (ระหว่างอุปกรณ์ที่โฮสต์) โฮสต์ (เริ่มต้นโดยโค้ด JavaScript ของคุณ) จะส่งข้อความที่มีการแจ้งว่า "ขาเข้า" ไปยังอุปกรณ์ รายละเอียดของข้อความจะขึ้นอยู่กับอุปกรณ์ แต่โดยทั่วไปจะมีการระบุสิ่งที่คุณขอจากอุปกรณ์นั้นๆ จากนั้นอุปกรณ์ตอบสนองด้วยข้อมูลที่ร้องขอ การตอบสนองของอุปกรณ์ได้รับการจัดการโดย Chrome และส่งให้กับโค้ดเรียกกลับที่คุณระบุในวิธีการโอนแบบไม่พร้อมกัน ข้อความขาออก (โฮสต์ไปยังอุปกรณ์) จะคล้ายคลึงกัน แต่การตอบกลับไม่มีข้อมูลที่ส่งคืนจากอุปกรณ์

สำหรับแต่ละข้อความจากอุปกรณ์ โค้ดเรียกกลับที่ระบุจะได้รับออบเจ็กต์เหตุการณ์ที่มีคุณสมบัติต่อไปนี้

พร็อพเพอร์ตี้คำอธิบาย
รหัสผลลัพธ์ (จำนวนเต็ม)0 หมายถึงสำเร็จ ค่าอื่นๆ หมายถึงล้มเหลว จะ
อ่านสตริงข้อผิดพลาดได้จาก chrome.extension.lastError เมื่อระบุความล้มเหลว
แล้ว
ข้อมูล (Arraybuffer)มีข้อมูลที่ส่งโดยอุปกรณ์หากการโอนเป็นขาเข้า

ตัวอย่าง

var onTransferCallback = function(event) {
   if (event && event.resultCode === 0 && event.data) {
     console.log("got " + event.data.byteLength + " bytes");
   }
};

chrome.usb.bulkTransfer(connectionHandle, transferInfo, onTransferCallback);

การควบคุมการโอน

โดยทั่วไป การโอนการควบคุมจะใช้เพื่อส่งหรือรับพารามิเตอร์การกำหนดค่าหรือคำสั่งไปยังอุปกรณ์ USB เมธอด ControlTransfer จะส่งไปยัง/อ่านจากปลายทาง 0 เสมอ และไม่จำเป็นต้องมี claimInterface วิธีการนี้ไม่ซับซ้อนและได้รับพารามิเตอร์ 3 รายการ ได้แก่

chrome.usb.controlTransfer(connectionHandle, transferInfo, transferCallback)
พารามิเตอร์ (ประเภท)คำอธิบาย
connectionHandleได้รับออบเจ็กต์ในโค้ดเรียกกลับ usb.openDevice
transferInfoออบเจ็กต์พารามิเตอร์ที่มีค่าจากตารางด้านล่าง โปรดดูรายละเอียดในข้อมูลจำเพาะของโปรโตคอลอุปกรณ์ USB
transferCallback()เรียกใช้เมื่อการโอนเสร็จสมบูรณ์

ค่าสำหรับออบเจ็กต์ transferInfo รายการ:

ค่าคำอธิบาย
requestType (สตริง)"vendor", "standard", "class" หรือ "สำรอง"
ผู้รับ (สตริง)"device" "อินเทอร์เฟซ" "ปลายทาง" หรือ "อื่นๆ"
ทิศทาง (สตริง)"in" หรือ "out" ทิศทาง "in" จะใช้เพื่อแจ้งอุปกรณ์ว่า
ควรส่งข้อมูลไปยังโฮสต์ การสื่อสารทั้งหมดในบัส USB
จะเริ่มต้นโดยโฮสต์ ดังนั้นให้ใช้การโอน "เข้า" เพื่อให้อุปกรณ์
ส่งข้อมูลกลับไปได้
คำขอ (จำนวนเต็ม)กำหนดโดยโปรโตคอลของอุปกรณ์
ค่า (จำนวนเต็ม)กำหนดโดยโปรโตคอลของอุปกรณ์
ดัชนี (จำนวนเต็ม)กำหนดโดยโปรโตคอลของอุปกรณ์
ความยาว (จำนวนเต็ม)ใช้เมื่อทิศทางเป็น "ใน" เท่านั้น แจ้งอุปกรณ์ว่านี่คือปริมาณข้อมูลที่โฮสต์คาดว่าจะตอบสนอง
ข้อมูล (Arraybuffer)กำหนดโดยโปรโตคอลของอุปกรณ์ จำเป็นเมื่อทิศทางเป็น "out"

ตัวอย่าง

var transferInfo = {
  "requestType": "vendor",
   "recipient": "device",
  "direction": "out",
  "request":  0x31,
  "value": 120,
  "index": 0,
  // Note that the ArrayBuffer, not the TypedArray itself is used.
  "data": new Uint8Array([4, 8, 15, 16, 23, 42]).buffer
};
chrome.usb.controlTransfer(connectionHandle, transferInfo, optionalCallback);

การโอน ISOCHRONOUS

การโอนแบบอิสระเป็นการโอนข้อมูล USB ประเภทที่ซับซ้อนที่สุด ซึ่งมักใช้กับสตรีมข้อมูล เช่น วิดีโอและเสียง หากต้องการเริ่มการโอนแบบอิสระ (ขาเข้าหรือขาออก) คุณต้องใช้เมธอด usb.isochronousTransfer ดังนี้

chrome.usb.isochronousTransfer(connectionHandle, isochronousTransferInfo, transferCallback)
พารามิเตอร์คำอธิบาย
connectionHandleได้รับออบเจ็กต์ในโค้ดเรียกกลับ usb.openDevice
isochronousTransferInfoออบเจ็กต์พารามิเตอร์ที่มีค่าในตารางด้านล่าง
transferCallback()เรียกใช้เมื่อการโอนเสร็จสมบูรณ์

ค่าสำหรับออบเจ็กต์ isochronousTransferInfo รายการ:

ค่าคำอธิบาย
TransferInfo (ออบเจ็กต์)ออบเจ็กต์ที่มีแอตทริบิวต์ต่อไปนี้
direction (string): "in" หรือ "out"
endpoint (integer): อุปกรณ์ของคุณกำหนด โดยปกติแล้วจะหาได้โดยการดูที่เครื่องมือตรวจวัดผ่าน USB เช่น lsusb -v
ความยาว (จำนวนเต็ม): จะใช้เมื่อทิศทางเป็น "in" เท่านั้น แจ้งอุปกรณ์ว่านี่คือปริมาณข้อมูลที่โฮสต์คาดว่าจะตอบสนอง
ควรอยู่ที่อย่างน้อย packets × packetLength
ข้อมูล (arraybuffer): ซึ่งกำหนดโดยโปรโตคอลของอุปกรณ์ จะใช้เมื่อทิศทางเป็น "out" เท่านั้น
แพ็กเก็ต (จำนวนเต็ม)จำนวนแพ็กเกตทั้งหมดที่คาดไว้ในการโอนนี้
packLength (จำนวนเต็ม)ระยะเวลาที่คาดไว้ของแต่ละแพ็กเก็ตในการโอนนี้

ตัวอย่าง

var transferInfo = {
  "direction": "in",
  "endpoint": 1,
  "length": 2560
};

var isoTransferInfo = {
  "transferInfo": transferInfo,
  "packets": 20,
  "packetLength": 128
};

chrome.usb.isochronousTransfer(connectionHandle, isoTransferInfo, optionalCallback);

เปลี่ยนยานพาหนะหลายครั้ง

การโอนจำนวนมากมักใช้ในการโอนข้อมูลจำนวนมากที่ไม่ละเอียดอ่อนต่อเวลาด้วยวิธีที่เชื่อถือได้ usb.bulkTransfer มีพารามิเตอร์ 3 ตัว ได้แก่

chrome.usb.bulkTransfer(connectionHandle, transferInfo, transferCallback);
พารามิเตอร์คำอธิบาย
connectionHandleได้รับออบเจ็กต์ในโค้ดเรียกกลับ usb.openDevice
transferInfoออบเจ็กต์พารามิเตอร์ที่มีค่าในตารางด้านล่าง
transferCallbackเรียกใช้เมื่อการโอนเสร็จสมบูรณ์

ค่าสำหรับออบเจ็กต์ transferInfo รายการ:

ค่าคำอธิบาย
ทิศทาง (สตริง)"in" หรือ "out"
ปลายทาง (จำนวนเต็ม)กำหนดโดยโปรโตคอลของอุปกรณ์
ความยาว (จำนวนเต็ม)ใช้เมื่อทิศทางเป็น "ใน" เท่านั้น แจ้งอุปกรณ์ว่านี่คือปริมาณข้อมูลที่โฮสต์คาดว่าจะตอบสนอง
ข้อมูล (ArrayBuffer)กำหนดโดยโปรโตคอลของอุปกรณ์ ใช้เมื่อทิศทางเป็น "out" เท่านั้น

ตัวอย่าง

var transferInfo = {
  "direction": "out",
  "endpoint": 1,
  "data": new Uint8Array([4, 8, 15, 16, 23, 42]).buffer
};

การโอนเงินมีปัญหา

การโอนข้อมูลที่หยุดชะงักจะใช้กับข้อมูลที่ละเอียดอ่อนในช่วงเวลาสั้นๆ เนื่องจากการสื่อสารผ่าน USB ทั้งหมดเริ่มต้นโดยโฮสต์ ปกติแล้วโค้ดโฮสต์จะสำรวจอุปกรณ์เป็นระยะๆ และส่งข้อมูลการรบกวนในการโอนซึ่งจะทำให้อุปกรณ์ส่งข้อมูลกลับคืนมาหากมีสิ่งใดอยู่ในคิวการขัดจังหวะ (เก็บรักษาโดยอุปกรณ์) usb.interruptTransfer มีพารามิเตอร์ 3 ตัว ได้แก่

chrome.usb.interruptTransfer(connectionHandle, transferInfo, transferCallback);
พารามิเตอร์คำอธิบาย
connectionHandleได้รับออบเจ็กต์ในโค้ดเรียกกลับ usb.openDevice
transferInfoออบเจ็กต์พารามิเตอร์ที่มีค่าในตารางด้านล่าง
transferCallbackเรียกใช้เมื่อการโอนเสร็จสมบูรณ์ โปรดทราบว่าโค้ดเรียกกลับนี้ไม่มีการตอบสนองของอุปกรณ์ วัตถุประสงค์ของโค้ดเรียกกลับคือการแจ้งโค้ดของคุณว่าคำขอการโอนแบบอะซิงโครนัสได้รับการดำเนินการแล้ว

ค่าสำหรับออบเจ็กต์ transferInfo รายการ:

ค่าคำอธิบาย
ทิศทาง (สตริง)"in" หรือ "out"
ปลายทาง (จำนวนเต็ม)กำหนดโดยโปรโตคอลของอุปกรณ์
ความยาว (จำนวนเต็ม)ใช้เมื่อทิศทางเป็น "ใน" เท่านั้น แจ้งอุปกรณ์ว่านี่คือปริมาณข้อมูลที่โฮสต์คาดว่าจะตอบสนอง
ข้อมูล (ArrayBuffer)กำหนดโดยโปรโตคอลของอุปกรณ์ ใช้เมื่อทิศทางเป็น "out" เท่านั้น

ตัวอย่าง

var transferInfo = {
  "direction": "in",
  "endpoint": 1,
  "length": 2
};
chrome.usb.interruptTransfer(connectionHandle, transferInfo, optionalCallback);

คำเตือน

เข้าถึงอุปกรณ์บางอย่างผ่าน USB API ไม่ได้ โดยทั่วไปแล้วจะไม่สามารถเข้าถึงอุปกรณ์ได้ เนื่องจากเคอร์เนลของระบบปฏิบัติการหรือไดรเวอร์ดั้งเดิมอาจทำให้อุปกรณ์ไม่สามารถเข้าถึงจากรหัสพื้นที่ของผู้ใช้ เช่น อุปกรณ์ที่มีโปรไฟล์ HID ในระบบ OSX และไดรฟ์ปากกา USB

ในระบบ Linux ส่วนใหญ่ อุปกรณ์ USB จะจับคู่ด้วยสิทธิ์อ่านอย่างเดียวโดยค่าเริ่มต้น หากต้องการเปิดอุปกรณ์ผ่าน API นี้ ผู้ใช้จะต้องมีสิทธิ์เขียนในอุปกรณ์ดังกล่าวด้วย วิธีแก้ไขง่ายๆ คือ ตั้งค่ากฎ udev สร้างไฟล์ /etc/udev/rules.d/50-yourdevicename.rules ที่มีเนื้อหาต่อไปนี้

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

จากนั้นรีสตาร์ท udev daemon service udev restart คุณสามารถตรวจสอบว่าตั้งค่าสิทธิ์ของอุปกรณ์ไว้ถูกต้องหรือไม่โดยทำตามขั้นตอนต่อไปนี้

  • เรียกใช้ lsusb เพื่อหาหมายเลขรถเมล์และอุปกรณ์
  • เรียกใช้ ls -al /dev/bus/usb/[bus]/[device] ไฟล์นี้ควรเป็นของกลุ่ม "plugdev" และมีสิทธิ์เขียนแบบกลุ่ม

แอปของคุณดําเนินการนี้โดยอัตโนมัติไม่ได้ เนื่องจากกระบวนการนี้ต้องมีสิทธิ์เข้าถึงรูท เราขอแนะนำให้คุณแจ้งวิธีการแก่ผู้ใช้ปลายทางและลิงก์ไปที่ส่วนคำเตือนในหน้านี้เพื่อดูคำอธิบาย

ใน ChromeOS เพียงเรียกใช้ usb.requestAccess ซึ่งตัวแทนที่ได้รับอนุญาตจะดำเนินการดังกล่าวให้คุณ