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

تتيح واجهة برمجة التطبيقات 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 تتيح هذه الميزة في الإصدار 106 من Chrome أو الإصدارات الأحدث.

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، في وضع البرمجة إذا تم تبديل إشارة &quot;محطة البيانات جاهزة&quot; (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.

للتعامل مع ذلك، يمكنك استخدام بعض عمليات نقل البيانات المضمّنة، مثل 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 Developer، ستستخدم Web Serial API للتفاعل مع لوحة BBC micro:bit لعرض الصور على مصفوفة LED بحجم 5x5.

دعم المتصفح

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

Polyfill

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

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

صمّم مطوّرو المواصفات ونفّذوا 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 وإخبارنا بمكان استخدامك لهذه الميزة وكيفية استخدامها.

روابط مفيدة

العروض التوضيحية

الإقرارات

نشكر رايلي غرانت وجو ميدلي على مراجعاتهما لهذه المقالة. صورة مصنع طائرات من مؤسسة متاحف برمنغهام على Unsplash