Web Serial API

2023.1.7

Web Serial APIはW3C Web Platform Incubator Community Groupで策定が進んでいるAPIです。今のところはW3Cの標準では無くドラフトとのことです。Arduinoのシリアル受信に便利なのではと思い試用してみました。

概要
シリアル API は、Web サイトがスクリプトを介してシリアル デバイスから読み書きする方法を提供します。 このAPI は、ブラウザがシリアルデバイスと通信できるようにすることで、Web と物理的な世界を橋渡しします。

caniuse.comに寄ると、現状ではChromeとEdgeのみが対応しているとの事です。



Serial Data受信

jquery.min.jsを使っているものとします。HTMLにシリアルのポートを開くボタンと、シリアルデータを読み込むためボタンを準備しておきます。

<button id="serial_start">Start</button>
<button id="serial_read">Read</button>

window.onloadでbuttonがクリックされたことを受けて、シリアルのポートを開きます。

function startpoart()
{
    navigator.serial.requestPort().then((port) => {
        // port`に接続した
        console.log("Ready Serial Port");
        mLog.innerText="Ready Serial Port\n";
        mPort=port;
        // portをボーレート112500bpsで開く
        port.open({ baudRate:112500 });
    }).catch((e) => {
        // error
        console.log("error");
    });
}

データを読み込むgetportはsync関数として定義します。portを開く、ポートからデータを読み込む関数は即結果が戻ってくるわけでは無いので、awaitで完了するまで待ちを入れて置きます。シリアル通信のボーレートを115200bpsに設定しています。

async function getport(port)
{
    while (port.readable) {
        const reader = port.readable.getReader();
        try {
            while (true) {
                const { value, done } = await reader.read();
                if (done) {
                // |reader| が中止になった。
                break;
                }
                // |value|に読み込んだデータが入っている。
                // consoleに書き出す
                console.log(value);
            }
        } catch (error) {
            // Handle |error|...
        } finally {
            reader.releaseLock();
          }
    }
}

参考ですが、getportをsync関数として定義して置かないと以下のエラーが出ます。

Uncaught SyntaxError: await is only valid in async functions and the top level bodies of modules
Arduino Leonardのプログラム

Arduino Leonardを接続ます。プログラムは以下の通りです。loop関数で、1バイトの変数xをwrite関数で出力し、インクリメントしています。

uint8_t x=0;
void setup() {
    Serial.begin(115200);
}
void loop() {
    Serial.write(x);
    x++;
    delay(1000);
}

作成したHTMLを立ち上げると下のようなダイアログが表示され、接続できるポートが表示されます。



接続ポートを選択し、接続ボタンを押すと受信を開始します。



consoleを開くと以下のようにシリアル通信を受信できています。

Uint8Array?[131, buffer: ArrayBuffer(1), byteLength: 1, byteOffset: 0, length: 1, Symbol(Symbol.toStringTag): 'Uint8Array']
Uint8Array?[132, buffer: ArrayBuffer(1), byteLength: 1, byteOffset: 0, length: 1, Symbol(Symbol.toStringTag): 'Uint8Array']
Uint8Array?[133, buffer: ArrayBuffer(1), byteLength: 1, byteOffset: 0, length: 1, Symbol(Symbol.toStringTag): 'Uint8Array']
Uint8Array?[134, buffer: ArrayBuffer(1), byteLength: 1, byteOffset: 0, length: 1, Symbol(Symbol.toStringTag): 'Uint8Array']
Uint8Array?[135, buffer: ArrayBuffer(1), byteLength: 1, byteOffset: 0, length: 1, Symbol(Symbol.toStringTag): 'Uint8Array']
Uint8Array?[136, buffer: ArrayBuffer(1), byteLength: 1, byteOffset: 0, length: 1, Symbol(Symbol.toStringTag): 'Uint8Array']
Uint8Array?[137, buffer: ArrayBuffer(1), byteLength: 1, byteOffset: 0, length: 1, Symbol(Symbol.toStringTag): 'Uint8Array']

Serial Data送信

2023.1.8

HTMLに貼り付けたボタンを押すと、シリアルデータを送信します。事前にポートはnavigator.serial.requestPort()関数で開かれているものとします。

<button id="serial_write">Serial Write</button>

送信するデータは受信と同じでUint8Arrayの型にしないといけないようです。

async function writepoart(port)
{
    const writer = port.writable.getWriter();
    const uint8 = Uint8Array.from('12345');
    await writer.write(uint8);
    writer.releaseLock();		
}

送信を確認するためのArduinoプログラムは以下の通りです。Arduino側で受け取ったデータをそのまま戻しています。

void setup() {
  Serial.begin(115200);
}

void loop() {
  uint8_t xx;
  while(1)
  if (Serial.available() > 0) {
    xx=Serial.read();
    Serial.write(xx);
  }
}
プログラムサンプル

このページの動作確認で使っているプログラムサンプルをまとめてこちらに置きます。


動作確認

  1. Startボタンを押して下さい。シリアルポートを選択するダイアログが表示されます。シリアルポートを選択して、”接続”を押して下さい。
  2. Readボタンを押して下さい。シリアルデー受信を開始します。下に受け取ったバイトデータを表示します。
  3. Writeボタンを押して下さい。右隣の値(Value)をシリアル送信します。
  4. Startボタン、Readボタンは2回押さないで下さい。エラーになります。


Serial: Value: