القراءة من منفذ تسلسلي والكتابة فيه

تتيح Web Serial API للمواقع الإلكترونية الاتصال بالأجهزة التسلسلية.

François Beaufort
François Beaufort

ما هي Web Serial API؟

المنفذ التسلسلي هو واجهة اتصال ثنائية الاتجاه تسمح بإرسال تلقي البيانات بالبايت.

توفر واجهة برمجة التطبيقات Web Serial API طريقة لمواقع الويب للقراءة والكتابة في جهاز تسلسلي باستخدام JavaScript. يتم ربط الأجهزة التسلسلية إما من خلال منفذ تسلسلي في نظام المستخدم أو من خلال أجهزة USB والبلوتوث قابلة للإزالة تحاكي منفذًا تسلسليًا.

بعبارة أخرى، تربط واجهة برمجة التطبيقات Web Serial API بين الويب والعالم الفعلي من خلال السماح لمواقع الويب بالاتصال بأجهزة تسلسلية، مثل وحدات التحكم الدقيقة والطابعات الثلاثية الأبعاد.

تُعد واجهة برمجة التطبيقات هذه أيضًا مرفقًا رائعًا لـ WebUSB حيث تتطلب أنظمة التشغيل الاتصال ببعض المنافذ التسلسلية باستخدام مستويات واجهة برمجة تطبيقات تسلسلية بدلاً من واجهة برمجة تطبيقات 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 غير متزامنة من حيث التصميم. يمنع هذا واجهة مستخدم موقع الويب من الحجب عند انتظار الإدخال، وهو أمر مهم لأنه يمكن في أي وقت، الأمر الذي يتطلب طريقة للاستماع إليها.

لفتح منفذ تسلسلي، يمكنك أولاً الوصول إلى عنصر 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 ميغابايت).
  • flowControl: وضع التحكّم في التدفق (إما "none" أو "hardware")

القراءة من منفذ تسلسلي

تعالج واجهة برمجة التطبيقات Streams API مصادر الإدخال والإخراج في واجهة برمجة التطبيقات Web Serial API.

بعد إنشاء اتصال المنفذ التسلسلي، readable وwritable من الكائن SerialPort تؤدي إلى إرجاع ReadableStream WritableStream. سيتم استخدام تلك البيانات لتلقّي بيانات من الأجهزة جهاز تسلسلي. تستخدم كلتا المثيلات Uint8Array لنقل البيانات.

عند وصول بيانات جديدة من الجهاز التسلسلي، port.readable.getReader().read() تعرض خاصيتين بشكل غير متزامن: 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 إلى وضع برمجة إذا كانت إشارة جاهزة لمحطة البيانات (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 أو إنشاء مجموعة بث تحويل تتيح لك تحليل البث الوارد وعرض البيانات المُحلّلة. يوجد تدفق التحويل بين الجهاز التسلسلي وحلقة القراءة التي تستهلك البث مُمْكِنْ تطبيق تحويل عشوائي قبل استهلاك البيانات. فكر في الأمر على أنه سطر التجميع: فعندما تأتي الأداة أسفل السطر، فإن كل خطوة في السطر تعدل الأداة، لذا عند وصولها إلى وجهتها النهائية، تصبح أداة تعمل بشكل صحيح.

صورة لمصنع طائرات
مصنع الطائرات في قلعة برومويتش من الحرب العالمية الثانية

على سبيل المثال، يمكنك الاطّلاع على كيفية إنشاء فئة بث مباشر تستخدم التحويل بثها وتقسيمها بناءً على فواصل الأسطر. ويُطلق على طريقة transform() اسم هذه الطريقة. في كل مرة يتلقى فيها مصدر البيانات بيانات جديدة يمكنه إما إدراج البيانات في قائمة الانتظار أو واحفظه لوقت لاحق. يتم استدعاء طريقة flush() عند إغلاق البث يعالج أي بيانات لم تتم معالجتها بعد.

لاستخدام فئة بث التحويل، يجب توجيه بث وارد من خلال بها. في مثال الرمز الثالث ضمن القسم القراءة من منفذ تسلسلي، تم توجيه تدفق الإدخال الأصلي عبر 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 لتقسيم مجموعات البث من الجهاز التسلسلي أو إليه. الاثنين يمكن استهلاكها بشكل مستقل عند إنشاء أحداث البث، ما يتيح لك طباعة محتوى إلى وحدة التحكم للفحص.

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() متوفّرة في الإصدار 103 من Chrome أو الإصدارات الأحدث، يُرجى التحقّق مما إذا كانت هذه الميزة متاحة متوافقة مع ما يلي:

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

درس تطبيقي حول الترميز

في الدرس التطبيقي حول ترميز مطوّر برامج Google، ستستخدم Web Serial API للتفاعل. باستخدام لوحة BBC Micro:bit لعرض الصور على مصفوفة LED بحجم 5x5.

دعم المتصفح

تتوفر واجهة برمجة التطبيقات Web Serial API على جميع الأنظمة الأساسية لأجهزة كمبيوتر سطح المكتب (ChromeOS وLinux وmacOS وWindows) في Chrome 89.

الملء التلقائي

على نظام التشغيل Android، يمكن استخدام المنافذ التسلسلية المستندة إلى USB باستخدام WebUSB API. ورمز polyfill لواجهة برمجة التطبيقات التسلسلية. تقتصر تعبئة polyfill هذه على الأجهزة التي يمكن الوصول من خلالها إلى الجهاز عبر واجهة برمجة تطبيقات WebUSB؛ تمت المطالبة بملكيته من خلال برنامج تشغيل مدمج في الجهاز.

الأمان والخصوصية

صمّم مؤلفو المواصفات واجهة برمجة التطبيقات Web Serial API ونفّذوها باستخدام المحددة في التحكُّم في الوصول إلى ميزات النظام الأساسي للويب الفعّالة بما في ذلك تحكُّم المستخدم والشفافية وتسهيل الاستخدام. القدرة على استخدام هذه تكون واجهة برمجة التطبيقات متوقفة بشكل أساسي من خلال نموذج أذونات يمنح إذن الوصول إلى مستخدم واحد فقط جهاز تسلسلي في المرة الواحدة. استجابةً لمطالبة المستخدم، يجب أن يفعّل المستخدم الخطوات لتحديد جهاز تسلسلي معين.

لمعرفة الإيجابيات والسلبيات المتعلّقة بالأمان، يُرجى الاطّلاع على قسم الأمان والخصوصية. أقسام Web Serial API التوضيحية.

ملاحظات

يودّ فريق Chrome معرفة رأيك وخبراتك في Web Serial API

أخبِرنا عن تصميم واجهة برمجة التطبيقات

هل هناك أي مشكلة في واجهة برمجة التطبيقات لا تعمل كما هو متوقع؟ أم أن هناك الطرق المفقودة أو الخصائص التي تحتاجها لتنفيذ فكرتك؟

يُرجى تقديم مشكلة في المواصفات في مستودع Web Serial API GitHub أو إضافة الأفكار بشأن مشكلة حالية.

الإبلاغ عن مشكلة في التنفيذ

هل واجهت مشكلة في التنفيذ في Chrome؟ أم أن التنفيذ عن المواصفات؟

يمكنك الإبلاغ عن الخطأ على https://new.crbug.com. تأكد من تضمين أكبر قدر قدر الإمكان، وتقديم إرشادات بسيطة لإعادة إنتاج الخطأ، تم ضبط المكوّنات على Blink>Serial. يعمل الخطأ بشكل رائع مشاركة عمليات إعادة الإنتاج السريعة والسهلة.

إظهار الدعم

هل تخطّط لاستخدام Web Serial API؟ يساعد دعمك العام متصفّح Chrome أولويات الفريق للميزات وتوضح لموردي المتصفح الآخرين مدى أهمية ودعمه.

إرسال تغريدة إلى @ChromiumDev باستخدام علامة التصنيف #SerialAPI عليك إعلامنا بمكان تطبيقك وطريقة استخدامه

روابط مفيدة

إصدارات تجريبية

شكر وتقدير

نشكر Reilly Grant وJoe Medley على مراجعاتهما لهذه المقالة. صورة من مصنع طائرات بواسطة صندوق متحف برمنغهام للطيران على موقع أنسبلاش