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

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

François Beaufort
François Beaufort

Web Serial API คืออะไร

พอร์ตอนุกรมเป็นอินเทอร์เฟซการสื่อสารแบบ 2 ทิศทางที่ช่วยให้สามารถส่งและรับไบต์ข้อมูลทีละไบต์ได้

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

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

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

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

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

ในบางกรณี เว็บไซต์จะสื่อสารกับอุปกรณ์ผ่านแอปพลิเคชัน Agent ที่ผู้ใช้ติดตั้งด้วยตัวเอง ขณะที่แอปพลิเคชันอื่นๆ จะส่งในแอปพลิเคชันแพ็กเกจผ่านเฟรมเวิร์ก เช่น 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() จะใช้ Object Literal ที่ไม่บังคับซึ่งกำหนดตัวกรอง ค่าเหล่านี้ใช้เพื่อจับคู่อุปกรณ์ซีเรียลที่เชื่อมต่อผ่าน 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 จะระบุความเร็วในการส่งข้อมูลผ่านบรรทัดอนุกรม โดยจะแสดงเป็นหน่วยบิตต่อวินาที (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")

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

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

หลังจากเชื่อมต่อพอร์ตอนุกรมแล้ว พร็อพเพอร์ตี้ readable และ writable จากออบเจ็กต์ SerialPort จะแสดงผล ReadableStream และ WritableStream ข้อมูลเหล่านั้นจะใช้ในการรับและส่งข้อมูลไปยังอุปกรณ์ซีเรียล ทั้งคู่ใช้อินสแตนซ์ 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);
}

คุณสามารถควบคุมการจัดสรรหน่วยความจำได้เมื่อคุณอ่านจากสตรีมโดยใช้เครื่องอ่าน "Bring Your Own Buffer" เรียกใช้ 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 Polyfill นี้จำกัดเฉพาะฮาร์ดแวร์และแพลตฟอร์ม ที่อุปกรณ์เข้าถึงได้ผ่าน WebUSB API เนื่องจากไม่ได้อ้างสิทธิ์โดยไดรเวอร์อุปกรณ์ในตัว

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

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

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

ความคิดเห็น

ทีม 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