อ่านและเขียนไปยังพอร์ตอนุกรม

Web Serial API ช่วยให้เว็บไซต์สื่อสารกับอุปกรณ์ซีเรียลได้

François Beaufort
François Beaufort

Web Serial API คืออะไร

พอร์ตอนุกรมคืออินเทอร์เฟซการสื่อสารแบบ 2 ทิศทางที่อนุญาตให้ส่งและรับข้อมูลทีละไบต์

Web Serial API เป็นวิธีที่ช่วยให้เว็บไซต์อ่านและเขียนไปยังอุปกรณ์ซีเรียลด้วย JavaScript ได้ อุปกรณ์ซีเรียลจะเชื่อมต่อผ่านพอร์ตอนุกรมในระบบของผู้ใช้ หรือผ่านอุปกรณ์ USB และบลูทูธแบบถอดได้ซึ่งจําลองพอร์ตอนุกรม

กล่าวคือ Web Serial API เป็นสะพานเชื่อมระหว่างเว็บกับโลกแห่งความเป็นจริงโดยอนุญาตให้เว็บไซต์สื่อสารกับอุปกรณ์ซีเรียล เช่น ไมโครคอนโทรลเลอร์และเครื่องพิมพ์ 3 มิติ

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

กรณีการใช้งานที่แนะนํา

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

ในบางกรณี เว็บไซต์จะสื่อสารกับอุปกรณ์ผ่านตัวแทนแอปพลิเคชันที่ผู้ใช้ติดตั้งด้วยตนเอง ส่วนแอปพลิเคชันอื่นๆ จะส่งในแอปพลิเคชันที่แพ็กเกจไว้ผ่านเฟรมเวิร์ก เช่น Electron และสำหรับอุปกรณ์บางรุ่น ผู้ใช้จะต้องทำตามขั้นตอนเพิ่มเติม เช่น การคัดลอกแอปพลิเคชันที่คอมไพล์แล้วไปยังอุปกรณ์ผ่าน USB แฟลชไดรฟ์

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

สถานะปัจจุบัน

ขั้นตอน สถานะ
1. สร้างคำอธิบาย เสร็จสมบูรณ์
2. สร้างฉบับร่างแรกของข้อกําหนด เสร็จสมบูรณ์
3. รวบรวมความคิดเห็นและปรับปรุงการออกแบบ เสร็จสมบูรณ์
4. ช่วงทดลองใช้จากต้นทาง เสร็จสมบูรณ์
5. เปิด เสร็จสมบูรณ์

การใช้ Web Serial API

การตรวจหาองค์ประกอบ

หากต้องการตรวจสอบว่าระบบรองรับ Web Serial API หรือไม่ ให้ใช้

if ("serial" in navigator) {
  // The Web Serial API is supported.
}

เปิดพอร์ตอนุกรม

Web Serial API เป็นแบบไม่พร้อมกันโดยการออกแบบ ซึ่งจะช่วยป้องกันไม่ให้ UI ของเว็บไซต์บล็อกเมื่อรออินพุต ซึ่งสำคัญเนื่องจากระบบอาจได้รับข้อมูลอนุกรมได้ทุกเมื่อ จึงต้องมีวิธีรับฟังข้อมูล

หากต้องการเปิดพอร์ตอนุกรม ให้เข้าถึงออบเจ็กต์ SerialPort ก่อน ในกรณีนี้ คุณสามารถขอให้ผู้ใช้เลือกพอร์ตซีเรียลรายการเดียวโดยเรียกใช้ navigator.serial.requestPort() เพื่อตอบสนองต่อท่าทางสัมผัสของผู้ใช้ เช่น การสัมผัสหรือการคลิกเมาส์ หรือเลือกพอร์ตจาก navigator.serial.getPorts() ซึ่งจะแสดงรายการพอร์ตซีเรียลที่เว็บไซต์ได้รับสิทธิ์เข้าถึง

document.querySelector('button').addEventListener('click', async () => {
  // Prompt user to select any serial port.
  const port = await navigator.serial.requestPort();
});
// Get all serial ports the user has previously granted the website access to.
const ports = await navigator.serial.getPorts();

ฟังก์ชัน navigator.serial.requestPort() จะรับออบเจ็กต์ลิเทอรัลที่ไม่บังคับซึ่งกำหนดตัวกรอง ซึ่งจะใช้จับคู่อุปกรณ์ซีเรียลที่เชื่อมต่อผ่าน USB กับผู้ให้บริการ USB (usbVendorId) ที่จำเป็นและตัวระบุผลิตภัณฑ์ USB (usbProductId) ที่ไม่บังคับ

// Filter on devices with the Arduino Uno USB Vendor/Product IDs.
const filters = [
  { usbVendorId: 0x2341, usbProductId: 0x0043 },
  { usbVendorId: 0x2341, usbProductId: 0x0001 }
];

// Prompt user to select an Arduino Uno device.
const port = await navigator.serial.requestPort({ filters });

const { usbProductId, usbVendorId } = port.getInfo();
ภาพหน้าจอของพรอมต์พอร์ตอนุกรมในเว็บไซต์
พรอมต์ของผู้ใช้สำหรับเลือก BBC micro:bit

การเรียกใช้ requestPort() จะแจ้งให้ผู้ใช้เลือกอุปกรณ์และแสดงผลออบเจ็กต์ SerialPort เมื่อคุณมีออบเจ็กต์ SerialPort แล้ว การเรียกใช้ port.open() ด้วยอัตราบอดที่ต้องการจะเปิดพอร์ตอนุกรม baudRate dictionary member จะระบุความเร็วในการส่งข้อมูลผ่านบรรทัดซีเรียล โดยอัตราบิตจะแสดงเป็นหน่วยบิตต่อวินาที (bps) โปรดดูค่าที่ถูกต้องในเอกสารประกอบของอุปกรณ์ เนื่องจากข้อมูลทั้งหมดที่คุณส่งและรับจะเป็นคำที่ไม่มีความหมายหากระบุค่านี้ไม่ถูกต้อง สำหรับอุปกรณ์ USB และบลูทูธบางรุ่นที่จำลองพอร์ตซีเรียล คุณสามารถตั้งค่านี้เป็นค่าใดก็ได้เนื่องจากระบบจะไม่สนใจค่านี้

// Prompt user to select any serial port.
const port = await navigator.serial.requestPort();

// Wait for the serial port to open.
await port.open({ baudRate: 9600 });

นอกจากนี้ คุณยังระบุตัวเลือกใดก็ได้ด้านล่างเมื่อเปิดพอร์ตซีเรียล ตัวเลือกเหล่านี้เป็นตัวเลือกที่ไม่บังคับและมีค่าเริ่มต้นที่สะดวก

  • dataBits: จำนวนบิตข้อมูลต่อเฟรม (7 หรือ 8)
  • stopBits: จํานวนบิตหยุดที่ท้ายเฟรม (1 หรือ 2)
  • parity: โหมดพาริตี ("none", "even" หรือ "odd")
  • bufferSize: ขนาดของบัฟเฟอร์การอ่านและเขียนที่ควรสร้างขึ้น (ต้องน้อยกว่า 16 MB)
  • flowControl: โหมดการควบคุมโฟลว์ ("none" หรือ "hardware")

อ่านจากพอร์ตอนุกรม

Streams API จะจัดการสตรีมอินพุตและเอาต์พุตใน Web Serial API

หลังจากสร้างการเชื่อมต่อพอร์ตซีเรียลแล้ว พร็อพเพอร์ตี้ readable และ writable จากออบเจ็กต์ SerialPort จะแสดงผล ReadableStream และ WritableStream ซึ่งจะใช้รับข้อมูลจากและส่งข้อมูลไปยังอุปกรณ์ซีเรียล ทั้ง 2 รายการใช้อินสแตนซ์ Uint8Array สำหรับการโอนข้อมูล

เมื่อข้อมูลใหม่มาจากอุปกรณ์ซีเรียล port.readable.getReader().read() จะแสดงผลพร็อพเพอร์ตี้ 2 รายการแบบไม่พร้อมกัน ได้แก่ value และบูลีน done หาก done เป็นจริง แสดงว่าพอร์ตซีเรียลปิดอยู่หรือไม่มีข้อมูลเข้ามาอีก การเรียกใช้ port.readable.getReader() จะสร้างโปรแกรมอ่านและล็อก readable ไว้กับโปรแกรมอ่าน ขณะที่ readable ล็อกอยู่ พอร์ตซีเรียลจะปิดไม่ได้

const reader = port.readable.getReader();

// Listen to data coming from the serial device.
while (true) {
  const { value, done } = await reader.read();
  if (done) {
    // Allow the serial port to be closed later.
    reader.releaseLock();
    break;
  }
  // value is a Uint8Array.
  console.log(value);
}

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

while (port.readable) {
  const reader = port.readable.getReader();

  try {
    while (true) {
      const { value, done } = await reader.read();
      if (done) {
        // Allow the serial port to be closed later.
        reader.releaseLock();
        break;
      }
      if (value) {
        console.log(value);
      }
    }
  } catch (error) {
    // TODO: Handle non-fatal read error.
  }
}

หากอุปกรณ์ซีเรียลส่งข้อความกลับมา คุณสามารถส่ง port.readable ผ่าน TextDecoderStream ดังที่แสดงด้านล่าง TextDecoderStream คือ สตรีมการเปลี่ยนรูปแบบ ที่ดึงข้อมูล Uint8Array ทั้งหมดและแปลงเป็นสตริง

const textDecoder = new TextDecoderStream();
const readableStreamClosed = port.readable.pipeTo(textDecoder.writable);
const reader = textDecoder.readable.getReader();

// Listen to data coming from the serial device.
while (true) {
  const { value, done } = await reader.read();
  if (done) {
    // Allow the serial port to be closed later.
    reader.releaseLock();
    break;
  }
  // value is a string.
  console.log(value);
}

คุณควบคุมวิธีจัดสรรหน่วยความจำได้เมื่ออ่านจากสตรีมโดยใช้โปรแกรมอ่านแบบ "นำบัฟเฟอร์ของคุณเองมา" เรียกใช้ port.readable.getReader({ mode: "byob" }) เพื่อรับอินเทอร์เฟซ ReadableStreamBYOBReader และระบุ ArrayBuffer ของคุณเองเมื่อเรียกใช้ read() โปรดทราบว่า Web Serial API รองรับฟีเจอร์นี้ใน Chrome เวอร์ชัน 106 ขึ้นไป

try {
  const reader = port.readable.getReader({ mode: "byob" });
  // Call reader.read() to read data into a buffer...
} catch (error) {
  if (error instanceof TypeError) {
    // BYOB readers are not supported.
    // Fallback to port.readable.getReader()...
  }
}

ตัวอย่างวิธีนําบัฟเฟอร์จาก value.buffer มาใช้ซ้ำมีดังนี้

const bufferSize = 1024; // 1kB
let buffer = new ArrayBuffer(bufferSize);

// Set `bufferSize` on open() to at least the size of the buffer.
await port.open({ baudRate: 9600, bufferSize });

const reader = port.readable.getReader({ mode: "byob" });
while (true) {
  const { value, done } = await reader.read(new Uint8Array(buffer));
  if (done) {
    break;
  }
  buffer = value.buffer;
  // Handle `value`.
}

ต่อไปนี้เป็นตัวอย่างวิธีอ่านข้อมูลจำนวนหนึ่งจากพอร์ตซีเรียล

async function readInto(reader, buffer) {
  let offset = 0;
  while (offset < buffer.byteLength) {
    const { value, done } = await reader.read(
      new Uint8Array(buffer, offset)
    );
    if (done) {
      break;
    }
    buffer = value.buffer;
    offset += value.byteLength;
  }
  return buffer;
}

const reader = port.readable.getReader({ mode: "byob" });
let buffer = new ArrayBuffer(512);
// Read the first 512 bytes.
buffer = await readInto(reader, buffer);
// Then read the next 512 bytes.
buffer = await readInto(reader, buffer);

เขียนไปยังพอร์ตอนุกรม

หากต้องการส่งข้อมูลไปยังอุปกรณ์ซีเรียล ให้ส่งข้อมูลไปยัง port.writable.getWriter().write() คุณต้องโทรหา releaseLock() ใน port.writable.getWriter() เพื่อให้พอร์ตอนุกรมปิดในภายหลัง

const writer = port.writable.getWriter();

const data = new Uint8Array([104, 101, 108, 108, 111]); // hello
await writer.write(data);


// Allow the serial port to be closed later.
writer.releaseLock();

ส่งข้อความไปยังอุปกรณ์ผ่าน TextEncoderStream ที่ส่งผ่านไปยัง port.writable ดังที่แสดงด้านล่าง

const textEncoder = new TextEncoderStream();
const writableStreamClosed = textEncoder.readable.pipeTo(port.writable);

const writer = textEncoder.writable.getWriter();

await writer.write("hello");

ปิดพอร์ตอนุกรม

port.close() จะปิดพอร์ตอนุกรมหากสมาชิก readable และ writable ปลดล็อกอยู่ ซึ่งหมายความว่ามีการเรียกใช้ releaseLock() สำหรับโปรแกรมอ่านและโปรแกรมเขียนที่เกี่ยวข้อง

await port.close();

อย่างไรก็ตาม เมื่ออ่านข้อมูลจากอุปกรณ์ซีเรียลอย่างต่อเนื่องโดยใช้ลูป port.readable จะล็อกอยู่เสมอจนกว่าจะพบข้อผิดพลาด ในกรณีนี้ การเรียกใช้ reader.cancel() จะบังคับให้ reader.read() แก้ปัญหากับ { value: undefined, done: true } ทันที และทำให้ลูปเรียกใช้ reader.releaseLock() ได้

// Without transform streams.

let keepReading = true;
let reader;

async function readUntilClosed() {
  while (port.readable && keepReading) {
    reader = port.readable.getReader();
    try {
      while (true) {
        const { value, done } = await reader.read();
        if (done) {
          // reader.cancel() has been called.
          break;
        }
        // value is a Uint8Array.
        console.log(value);
      }
    } catch (error) {
      // Handle error...
    } finally {
      // Allow the serial port to be closed later.
      reader.releaseLock();
    }
  }

  await port.close();
}

const closedPromise = readUntilClosed();

document.querySelector('button').addEventListener('click', async () => {
  // User clicked a button to close the serial port.
  keepReading = false;
  // Force reader.read() to resolve immediately and subsequently
  // call reader.releaseLock() in the loop example above.
  reader.cancel();
  await closedPromise;
});

การปิดพอร์ตอนุกรมจะซับซ้อนกว่าเมื่อใช้สตรีมการเปลี่ยนรูปแบบ โทรหา reader.cancel() เหมือนเดิม จากนั้นโทรหา writer.close() และ port.close() ซึ่งจะทำให้เกิดข้อผิดพลาดผ่านสตรีมการเปลี่ยนรูปแบบไปยังพอร์ตอนุกรมที่อยู่เบื้องหลัง เนื่องจากการนำข้อผิดพลาดไปข้างหน้าจะไม่เกิดขึ้นทันที คุณจึงต้องใช้สัญญา readableStreamClosed และ writableStreamClosed ที่สร้างขึ้นก่อนหน้านี้เพื่อตรวจหาเมื่อ port.readable และ port.writable ได้รับการปลดล็อกแล้ว การยกเลิก reader จะทําให้สตรีมหยุดลง คุณจึงต้องจับข้อผิดพลาดที่เกิดขึ้นและละเว้น

// With transform streams.

const textDecoder = new TextDecoderStream();
const readableStreamClosed = port.readable.pipeTo(textDecoder.writable);
const reader = textDecoder.readable.getReader();

// Listen to data coming from the serial device.
while (true) {
  const { value, done } = await reader.read();
  if (done) {
    reader.releaseLock();
    break;
  }
  // value is a string.
  console.log(value);
}

const textEncoder = new TextEncoderStream();
const writableStreamClosed = textEncoder.readable.pipeTo(port.writable);

reader.cancel();
await readableStreamClosed.catch(() => { /* Ignore the error */ });

writer.close();
await writableStreamClosed;

await port.close();

ฟังการเชื่อมต่อและการยกเลิกการเชื่อมต่อ

หากอุปกรณ์ USB มีพอร์ตอนุกรม อุปกรณ์ดังกล่าวอาจเชื่อมต่อหรือยกเลิกการเชื่อมต่อกับระบบ เมื่อเว็บไซต์ได้รับสิทธิ์เข้าถึงพอร์ตอนุกรมแล้ว ก็ควรตรวจสอบเหตุการณ์ connect และ disconnect

navigator.serial.addEventListener("connect", (event) => {
  // TODO: Automatically open event.target or warn user a port is available.
});

navigator.serial.addEventListener("disconnect", (event) => {
  // TODO: Remove |event.target| from the UI.
  // If the serial port was opened, a stream error would be observed as well.
});

จัดการสัญญาณ

หลังจากสร้างการเชื่อมต่อพอร์ตอนุกรมแล้ว คุณสามารถค้นหาและตั้งค่าสัญญาณที่แสดงโดยพอร์ตอนุกรมอย่างชัดเจนเพื่อการตรวจหาอุปกรณ์และการควบคุมโฟลว์ สัญญาณเหล่านี้จะกําหนดเป็นค่าบูลีน ตัวอย่างเช่น อุปกรณ์บางอย่างเช่น Arduino จะเข้าสู่โหมดการเขียนโปรแกรมหากมีการเปิด/ปิดสัญญาณ Data Terminal Ready (DTR)

การตั้งค่าสัญญาณเอาต์พุตและการรับสัญญาณอินพุตทำได้โดยเรียกใช้ port.setSignals() และ port.getSignals() ตามลำดับ ดูตัวอย่างการใช้งานด้านล่าง

// Turn off Serial Break signal.
await port.setSignals({ break: false });

// Turn on Data Terminal Ready (DTR) signal.
await port.setSignals({ dataTerminalReady: true });

// Turn off Request To Send (RTS) signal.
await port.setSignals({ requestToSend: false });
const signals = await port.getSignals();
console.log(`Clear To Send:       ${signals.clearToSend}`);
console.log(`Data Carrier Detect: ${signals.dataCarrierDetect}`);
console.log(`Data Set Ready:      ${signals.dataSetReady}`);
console.log(`Ring Indicator:      ${signals.ringIndicator}`);

การเปลี่ยนรูปแบบสตรีม

เมื่อได้รับข้อมูลจากอุปกรณ์ซีเรียล คุณอาจไม่ได้รับข้อมูลทั้งหมดพร้อมกัน โดยอาจมีการแบ่งออกเป็นกลุ่มๆ โปรดดูข้อมูลเพิ่มเติมที่หัวข้อแนวคิดเกี่ยวกับ Streams API

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

รูปภาพโรงงานผลิตเครื่องบิน
โรงงานผลิตเครื่องบิน Castle Bromwich ในสมัยสงครามโลกครั้งที่ 2

เช่น ลองดูวิธีสร้างคลาสสตรีมการเปลี่ยนรูปแบบที่ใช้สตรีม และแบ่งออกเป็นกลุ่มตามการขึ้นบรรทัดใหม่ ระบบจะเรียกใช้เมธอด transform() ทุกครั้งที่สตรีมได้รับข้อมูลใหม่ โดยสามารถจัดคิวข้อมูลหรือบันทึกไว้ใช้ในภายหลังก็ได้ ระบบจะเรียกใช้เมธอด flush() เมื่อสตรีมปิดอยู่ และเมธอดนี้จะจัดการข้อมูลที่ยังไม่ได้ประมวลผล

หากต้องการใช้คลาสสตรีมการเปลี่ยนรูปแบบ คุณต้องส่งผ่านสตรีมขาเข้าผ่านคลาสนี้ ในตัวอย่างโค้ดที่ 3 ในส่วนอ่านจากพอร์ตอนุกรม ระบบส่งผ่านสตรีมอินพุตเดิมผ่าน TextDecoderStream เท่านั้น เราจึงต้องเรียกใช้ pipeThrough() เพื่อส่งผ่านสตรีมผ่าน LineBreakTransformer ใหม่

class LineBreakTransformer {
  constructor() {
    // A container for holding stream data until a new line.
    this.chunks = "";
  }

  transform(chunk, controller) {
    // Append new chunks to existing chunks.
    this.chunks += chunk;
    // For each line breaks in chunks, send the parsed lines out.
    const lines = this.chunks.split("\r\n");
    this.chunks = lines.pop();
    lines.forEach((line) => controller.enqueue(line));
  }

  flush(controller) {
    // When the stream is closed, flush any remaining chunks out.
    controller.enqueue(this.chunks);
  }
}
const textDecoder = new TextDecoderStream();
const readableStreamClosed = port.readable.pipeTo(textDecoder.writable);
const reader = textDecoder.readable
  .pipeThrough(new TransformStream(new LineBreakTransformer()))
  .getReader();

หากต้องการแก้ไขข้อบกพร่องเกี่ยวกับการสื่อสารของอุปกรณ์ซีเรียล ให้ใช้เมธอด tee() ของ port.readable เพื่อแยกสตรีมไปยังหรือจากอุปกรณ์ซีเรียล สตรีม 2 รายการที่สร้างไว้จะใช้แยกกันได้ ซึ่งช่วยให้คุณพิมพ์สตรีมหนึ่งไปยังคอนโซลเพื่อตรวจสอบได้

const [appReadable, devReadable] = port.readable.tee();

// You may want to update UI with incoming data from appReadable
// and log incoming data in JS console for inspection from devReadable.

เพิกถอนสิทธิ์เข้าถึงพอร์ตอนุกรม

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

// Voluntarily revoke access to this serial port.
await port.forget();

เนื่องจาก forget() พร้อมใช้งานใน Chrome เวอร์ชัน 103 ขึ้นไป ให้ตรวจสอบว่าอุปกรณ์รองรับฟีเจอร์นี้หรือไม่โดยทำดังนี้

if ("serial" in navigator && "forget" in SerialPort.prototype) {
  // forget() is supported.
}

เคล็ดลับสำหรับนักพัฒนาซอฟต์แวร์

การแก้ไขข้อบกพร่องของ Web Serial API ใน Chrome ทำได้ง่ายด้วยหน้าภายใน ซึ่งabout://device-logคุณสามารถดูเหตุการณ์ทั้งหมดที่เกี่ยวข้องกับอุปกรณ์ซีเรียลได้ในที่เดียว

ภาพหน้าจอของหน้าภายในสำหรับการแก้ไขข้อบกพร่องของ Web Serial API
หน้าภายในใน Chrome สำหรับการแก้ไขข้อบกพร่องของ Web Serial API

Codelab

ใน Google Developer Codelab คุณจะใช้ Web Serial API เพื่อโต้ตอบกับบอร์ด BBC micro:bit เพื่อแสดงรูปภาพบนเมทริกซ์ LED 5x5

การสนับสนุนเบราว์เซอร์

Web Serial API พร้อมใช้งานบนแพลตฟอร์มเดสก์ท็อปทั้งหมด (ChromeOS, Linux, macOS และ Windows) ใน Chrome 89

โพลีฟิลล์

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

ความปลอดภัยและความเป็นส่วนตัว

ผู้เขียนข้อกำหนดได้ออกแบบและติดตั้งใช้งาน Web Serial API โดยใช้หลักการหลักที่ระบุไว้ในการควบคุมการเข้าถึงฟีเจอร์ที่มีประสิทธิภาพของแพลตฟอร์มเว็บ ซึ่งรวมถึงการควบคุมของผู้ใช้ ความโปร่งใส และลักษณะที่เหมาะกับการใช้งาน ความสามารถในการใช้ API นี้ถูกจำกัดโดยรูปแบบสิทธิ์ที่อนุญาตให้เข้าถึงอุปกรณ์ซีเรียลได้ครั้งละ 1 เครื่องเท่านั้น ผู้ใช้ต้องดำเนินการตามขั้นตอนเพื่อเลือกอุปกรณ์ซีเรียลที่ต้องการเพื่อตอบสนองต่อข้อความแจ้ง

หากต้องการทำความเข้าใจข้อเสียด้านความปลอดภัย โปรดดูส่วนความปลอดภัยและความเป็นส่วนตัวของคำอธิบาย Web Serial API

ความคิดเห็น

ทีม Chrome อยากทราบความคิดเห็นและประสบการณ์ของคุณเกี่ยวกับ Web Serial API

บอกเราเกี่ยวกับการออกแบบ API

มีสิ่งใดเกี่ยวกับ API ที่ไม่ทำงานตามที่คาดไว้ไหม หรือมีเมธอดหรือพร็อพเพอร์ตี้ที่ขาดหายไปซึ่งคุณต้องนำไปใช้กับไอเดียของคุณ

แจ้งปัญหาเกี่ยวกับข้อกำหนดใน ที่เก็บ GitHub ของ Web Serial API หรือแสดงความคิดเห็นในปัญหาที่มีอยู่

รายงานปัญหาเกี่ยวกับการติดตั้งใช้งาน

หากพบข้อบกพร่องในการใช้งาน Chrome หรือการใช้งานแตกต่างจากข้อกําหนดหรือไม่

รายงานข้อบกพร่องที่ https://new.crbug.com อย่าลืมระบุรายละเอียดให้มากที่สุด ระบุวิธีการง่ายๆ ในการทําให้เกิดข้อบกพร่องซ้ำ และตั้งค่าคอมโพเนนต์เป็น Blink>Serial Glitch เหมาะอย่างยิ่งสำหรับการแชร์การจำลองข้อบกพร่องที่รวดเร็วและง่ายดาย

แสดงการสนับสนุน

คุณวางแผนที่จะใช้ Web Serial API ใช่ไหม การสนับสนุนแบบสาธารณะของคุณจะช่วยให้ทีม Chrome จัดลำดับความสำคัญของฟีเจอร์ต่างๆ และแสดงให้เห็นว่าการสนับสนุนฟีเจอร์เหล่านี้สำคัญเพียงใดต่อผู้ให้บริการเบราว์เซอร์รายอื่นๆ

ทวีตถึง @ChromiumDev โดยใช้แฮชแท็ก #SerialAPI และแจ้งให้เราทราบว่าคุณใช้ฟีเจอร์นี้ที่ไหนและอย่างไร

ลิงก์ที่มีประโยชน์

เดโม

ขอขอบคุณ

ขอขอบคุณ Reilly Grant และ Joe Medley ที่ตรวจสอบบทความนี้ รูปภาพโรงงานผลิตเครื่องบินโดย Birmingham Museums Trust ใน Unsplash