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

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 อนุกรมระดับสูงกว่า แทนที่จะเป็น USB API ระดับต่ำ

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

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

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

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