WebシリアルAPIの使用方法

要するに、WebシリアルAPIを使用すると、ウェブ上で純粋なJavaScriptを使用して双方向のシリアル通信を実現できます。もちろん、組み込みシステムの開発かだい等にも使用できます。

詳細な使用法は公式ドキュメントを参照してください。この記事では基本的な使用法のみを紹介します。

環境要件

デスクトップ向けのChromiumのバージョン89以上が必要です。

最新版のChromeやEdge等を使用できます。IEやFirefoxはサポートされていません。

以下のコードを使用して、ブラウザがWebシリアルAPIをサポートしているかどうかをテストできます。

1
2
3
if ('serial' in navigator) {
    console.log('WebシリアルAPIをサポートしています');
}

コンソールにWebシリアルAPIをサポートしていますと表示されれば、ブラウザはWebシリアルAPIをサポートしています。

WebシリアルAPIの使用に関して、以下の注意事項もあります。

  • WebシリアルAPIは設計上非同期型です。
  • プロジェクトをオンラインにデプロイする場合、httpsプロトコルを使用する必要があります。ローカル環境ではこの制限はありません。
  • 通信はユーザーが明示的にトリガーするイベントにバインドする必要があり、自動的に通信を開始することは許可されていません。

これらを確認したら、次の手順に進みましょう。

シリアル接続の開始

先ず、シリアル権限を取得する必要があります。以下のコードはボタンが押された時にユーザープロンプトを表示し、ユーザーがシリアルポートを選択するようにします。

1
2
3
connectButton.addEventListener('click', async () => {
  const port = await navigator.serial.requestPort();
});

選択が完了したら、シリアルポートに接続することができます。

1
await port.open({ baudRate: 9600 });

このメソッドでは、ストップビット、データビット、パリティモード等のオプションも指定できますが、ここでは割愛します。

データの送受信

データの送受信には次のコードを使用できます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
// dataを送信
const writer = port.writable.getWriter();
await writer.write(data);
await writer.close();

// valueを受信し、コンソールに表示
const reader = port.readable.getReader();
while (true) {
    const { value, done } = await reader.read();
    if (done) {
        reader.releaseLock();
        break;
    }
    console.log(value);
}

上記のコードではUint8配列の送受信のみを扱います。文字列の送受信を行いたい場合は、TextEncoder及びTextDecoderを使用することができます。これらは文字列をUTF-8符号化及び復号できます。

1
2
3
4
5
6
7
8
9
// 文字列をUint8配列に符号化
const encoder = new TextEncoder();
const data = encoder.encode('Hello, world!');
console.log(data); // Uint8Array(13) [72, 101, 108, 108, 111, 44, 32, 119, 111, 114, 108, 100, 33]

// Uint8配列を文字列に復号
const decoder = new TextDecoder();
const value = decoder.decode(data);
console.log(value); // 'Hello, world!'

データの受信順序を確保するために、コードにデバウンスを追加する必要があります。また、通常は数百ミリ秒ごとに受信したデータを一度コンソールに出力する必要があります。

最終的なコード例は以下の通りです。なお、このコードには予期しない状況への対処が一切含まれていませんので、注意してください。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
let port; // シリアルポートオブジェクト
let debounceTimeout; // デバウンス遅延タイマー
let delayTime = 500; // 遅延時間

connectButton.addEventListener('click', () => {
    // 以前のデバウンス遅延タイマーをクリア
    clearTimeout(debounceTimeout);

    // 新しいデバウンス遅延タイマーを作成
    debounceTimeout = setTimeout(async () => {
        // シリアルポートに接続
        port = await navigator.serial.requestPort();
        await port.open({ baudRate: 9600 });
        console.log('Serial port connected');

        // シリアルデータを受信
        const reader = port.readable.getReader();
        const decoder = new TextDecoder();

        let receivedData = []; // 受信データを格納するための配列

        while (true) {
            const { value, done } = await reader.read();
            if (done) {
                reader.releaseLock();
                break;
            }

            receivedData = receivedData.concat(Array.from(value)); // 受信したバイトを配列に追加

            // 受信したデータを一度表示
            setTimeout(() => {
                const text = decoder.decode(new Uint8Array(receivedData));
                console.log(text);
                receivedData = []; // 受信したデータをクリア
            }, delayTime);
        }
    }, delayTime);
});

// データを送信
sendButton.addEventListener('click', async () => {
    const writer = port.writable.getWriter();
    const encoder = new TextEncoder();
    const data = encoder.encode('Hello, world!');
    await writer.write(data);
    await writer.close();
});

一歩前進

より複雑な使用法については、ぜひ公式ドキュメントを参照してください。

筆者はWebシリアルAPIを基にして、簡単なWebシリアルデバッグツールを作成しました。より多くのオプションを設定したり、UTF-8以外の符号化方式を使用したり、予期せぬ状況に対処したりする等の場合には、そのコードも参考にできます。