Web Serial APIでブラウザからシリアルデバイスを操作する
最近のウェブブラウザは、かつてはネイティブアプリケーションでしか実現できなかった機能を次々と実装しています。その中でも今回注目したいのが「Web Serial API」です。この API を使うことで、ウェブブラウザから直接シリアルデバイスと通信できるようになります。マイコンボードや IoT デバイスを制御するウェブアプリケーションが作れるなんて、ちょっと前までは考えられなかったですよね。
Web Serial API とは
Web Serial API は、ウェブアプリケーションからシリアルポートにアクセスするための JavaScript API です。これにより、Arduino、micro:bit、各種センサーなど、シリアル通信をサポートするハードウェアデバイスとブラウザから直接通信できるようになります。
従来、こういったハードウェアと通信するには:
- ネイティブアプリケーションを開発する
- 専用のドライバをインストールする
- サーバーを介して間接的に通信する
などの方法が必要でした。Web Serial API を使えば、ブラウザだけでこれらのデバイスと直接やり取りできるようになります。これはウェブプラットフォームの大きな進化だと思います。
対応ブラウザ
2025 年 3 月現在、Web Serial API は以下のブラウザで利用可能です:
- Google Chrome(バージョン 89 以降)
- Microsoft Edge(Chromium ベース、バージョン 89 以降)
- Opera(バージョン 76 以降)
残念ながら、Firefox、Safari、iOS 版の Chrome などでは現時点でサポートされていません。これはセキュリティやプライバシーの観点から、各ブラウザベンダーが慎重に対応を検討しているためです。
基本的な使い方
Web Serial API の基本的な使い方を見ていきましょう。シンプルな例として、シリアルポートに接続して、データを送受信する方法を紹介します。
1. シリアルポートへのアクセス要求
まず、ユーザーのアクションに応じてシリアルポートへのアクセスを要求します。これは重要なポイントで、ユーザーの明示的なアクション(クリックやタップなど)がないとシリアルポートにアクセスできません。
さらに重要なのは、このユーザーアクティベーションの有効期間です。Web Serial API では、ユーザーアクションが発生してから1 ~ 5 秒の間に API を呼び出す必要があります。この時間を超えると、「ユーザーアクティベーションが必要です」というエラーが発生します。
例えば、ユーザーがボタンをクリックした後に重い処理を実行し、それに 5 秒以上かかると、その後の API 呼び出しは失敗します。安全な実装としては、まずユーザーアクティベーションが有効な間に API を呼び出し、その後で時間のかかる処理を実行するようにしましょう。
// ボタンクリックなどのユーザーアクションに応じて実行
async function connectToSerialPort() {
try {
// シリアルポートを選択するダイアログを表示
// 注意: このメソッド呼び出しはユーザーアクションから1~5秒以内に行う必要がある
const port = await navigator.serial.requestPort();
// ポートを開く(ボーレートなどのオプションを指定)
await port.open({ baudRate: 9600 });
console.log("シリアルポートに接続しました!");
return port;
} catch (error) {
console.error("接続エラー:", error);
}
}
// 安全な実装例
document.getElementById("connectButton").addEventListener("click", async () => {
// まずユーザーアクティベーションが有効な間にAPIを呼び出す
const port = await connectToSerialPort();
// その後で時間のかかる処理を実行
await someHeavyProcessing();
// 以降の処理...
});
一度ポートへのアクセス許可を得た後は、そのポートインスタンスを保存しておけば、以降のページ表示では再度のユーザーアクションなしで接続できます。また、navigator.serial.getPorts()
メソッドを使用すると、以前にアクセスを許可したポートのリストを取得できます。
2. データの送信
シリアルポートにデータを送信するには、WritableStream
を使います。Web Serial API では、テキストデータだけでなく、バイナリデータ(バイト列)も送信できます。これは特にハードウェアデバイスとの通信では重要です。
async function writeToSerialPort(port, data) {
const writer = port.writable.getWriter();
// テキストデータをUint8Array(バイト配列)に変換
const encoder = new TextEncoder();
const dataArrayBuffer = encoder.encode(data);
// データを書き込む
await writer.write(dataArrayBuffer);
// writerを解放(他の処理でも書き込めるようにする)
writer.releaseLock();
}
バイナリデータを直接送信する場合は、以下のように実装できます:
async function writeBinaryToSerialPort(port, binaryData) {
const writer = port.writable.getWriter();
// バイナリデータの例(コマンドバイト列)
// 例: [0x02, 0x10, 0x03] のようなバイト列
const data = new Uint8Array(binaryData);
// バイナリデータを書き込む
await writer.write(data);
writer.releaseLock();
}
// 使用例
// writeBinaryToSerialPort(port, [0x02, 0x10, 0x03]);
多くのハードウェアデバイスは特定のバイト列をコマンドとして解釈するため、このようなバイナリ通信は非常に重要です。例えば、ロボットの制御やセンサーの設定変更などでは、テキストではなくバイナリプロトコルが使われることが一般的です。
3. データの受信
シリアルポートからデータを受信するには、ReadableStream
を使います。受信したデータは基本的にバイト列(Uint8Array)として取得され、必要に応じてテキストに変換できます。
async function readFromSerialPort(port) {
const reader = port.readable.getReader();
try {
while (true) {
const { value, done } = await reader.read();
if (done) {
// 読み取りが完了した(ポートが閉じられたなど)
break;
}
// 受信したデータ(Uint8Array)をテキストに変換
const decoder = new TextDecoder();
const text = decoder.decode(value);
console.log("受信データ(テキスト):", text);
// ここで受信データを処理する
}
} catch (error) {
console.error("読み取りエラー:", error);
} finally {
reader.releaseLock();
}
}
バイナリデータとして処理する場合は、TextDecoder を使わず、直接バイト列を扱います:
async function readBinaryFromSerialPort(port) {
const reader = port.readable.getReader();
try {
while (true) {
const { value, done } = await reader.read();
if (done) break;
// value は Uint8Array
console.log("受信バイナリデータ:", value);
// バイト列を直接処理する例
for (let i = 0; i < value.length; i++) {
// 各バイトに対する処理
const byte = value[i];
console.log(`バイト ${i}: ${byte.toString(16)}`); // 16進数で表示
// 特定のコマンドバイトを検出する例
if (byte === 0x02) {
console.log("開始バイトを検出");
} else if (byte === 0x03) {
console.log("終了バイトを検出");
}
}
}
} catch (error) {
console.error("読み取りエラー:", error);
} finally {
reader.releaseLock();
}
}
バイナリプロトコルを使用するデバイスでは、特定のバイトパターンを検出して適切に応答することが重要です。例えば、多くのセンサーは特定のコマンドバイトに続いてデータバイトを送信し、チェックサムで終わるようなプロトコルを使用しています。
4. 接続の終了
使い終わったら、ポートを閉じることを忘れないようにしましょう。
async function closeSerialPort(port) {
await port.close();
console.log("シリアルポートを閉じました");
}
実際のユースケース
Web Serial API を使うと、どんなことができるようになるのでしょうか?いくつか具体的なユースケースを紹介します。
マイコンボードのプログラミング環境
Arduino IDE のようなプログラミング環境をウェブブラウザで実現できます。コードを書いて、コンパイルし、そのままブラウザからマイコンボードに書き込むことが可能になります。実際、Arduino Web EditorやMakeCodeなどのオンラインエディタは、この技術を活用しています。
僕が開発したSketch Bridgeも Web Serial API を活用したサービスの一つです。Sketch Bridge は、Arduino のスケッチをブラウザから直接マイコンボードに書き込めるだけでなく、クラウド上でのコード管理も提供しています。特に教育現場やコミュニティでの利用を想定して開発しました。インストール不要で、Chromebook を含むどこからでもアクセスできる点が、従来の IDE と比べて大きなメリットになっています。
IoT デバイスの設定インターフェース
IoT デバイスの初期設定(Wi-Fi 接続情報の入力など)をブラウザから直接行えるようになります。ユーザーはアプリをインストールする必要がなく、QR コードを読み取ってウェブページにアクセスするだけで設定できるようになります。
データロガーやセンサーの監視ツール
温度センサーや加速度センサーなどからのデータをリアルタイムで取得し、ブラウザ上でグラフ化したり分析したりできます。従来はデータを SD カードに保存してから取り出す、あるいは専用アプリを使う必要がありましたが、ウェブブラウザだけで完結するようになります。
自作キーボードのカスタマイズツール
自作キーボードのキーマッピングや LED の設定などをブラウザから直接行えるようになります。僕自身、自作キーボードに興味があるので、この用途は特に魅力的です。Remapというツールでは、Web Serial API を使ってファムウェアのマイコンへの書き込みを行っています。
セキュリティ上の考慮点
Web Serial API は強力な機能ですが、同時にセキュリティ上のリスクも伴います。ブラウザベンダーはこれらのリスクを軽減するために、いくつかの制限を設けています:
- ユーザーの明示的なアクション(ボタンクリックなど)がないと API を使用できない
- HTTPS 接続のウェブサイトでのみ使用可能
- ユーザーがデバイスを選択するダイアログが表示される
- 接続を許可したデバイスの情報はブラウザに保存され、次回からは確認なしで接続可能(ただしユーザーはいつでも権限を取り消せる)
これらの制限は、悪意あるウェブサイトがユーザーの知らないうちにデバイスにアクセスすることを防ぐためのものです。
まとめ
Web Serial API は、ウェブブラウザとハードウェアデバイスの間の壁を取り払う革新的な技術です。これにより、従来はネイティブアプリケーションでしか実現できなかった多くの機能が、ウェブアプリケーションでも実現可能になります。
特に、マイコンボードや IoT デバイスの開発者にとって、この技術は大きな可能性を秘めています。プログラミング環境、設定ツール、モニタリングアプリなど、様々なアプリケーションをインストール不要で提供できるようになるからです。
ただし、まだすべてのブラウザでサポートされているわけではないので、実際に使用する際は対応状況を確認する必要があります。また、セキュリティ面での配慮も忘れないようにしましょう。
Web Serial API は、ウェブプラットフォームの進化の一例であり、ウェブとフィジカルワールドの融合がさらに進んでいくことを示しています。みなさんも、ぜひこの新しい技術を試してみてください!