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

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

เมื่อได้รับข้อมูลใหม่จากอุปกรณ์ซีเรียล port.readable.getReader().read() จะแสดงพร็อพเพอร์ตี้ 2 รายการแบบไม่พร้อมกัน ได้แก่ value และบูลีน done หาก done เป็นจริง แสดงว่าพอร์ตอนุกรมถูกปิดแล้วหรือไม่มีข้อมูลเข้ามาอีก การเรียกใช้ port.readable.getReader() จะสร้าง Reader และล็อก readable ไว้กับ Reader นั้น ขณะที่ 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 จะกลายเป็น null

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

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

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

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

Polyfill

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

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

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

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

ความคิดเห็น

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

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

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

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

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

คุณพบข้อบกพร่องในการใช้งาน Chrome ไหม หรือการติดตั้งใช้งาน แตกต่างจากข้อกำหนด

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

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

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

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

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

การสาธิต

คำขอบคุณ

ขอขอบคุณ Reilly Grant และ Joe Medley ที่รีวิวเอกสารนี้