JS `Web Serial API` (浏览器):与串口设备直接通信

各位观众老爷,大家好!今天咱们来聊聊一个听起来有点神秘,但其实挺接地气的玩意儿:Web Serial API。简单来说,就是让你的网页直接跟串口设备“勾搭”上,省去了中间商赚差价,啊不,是省去了复杂的驱动和插件。

一、 啥是 Web Serial API?

想象一下,你有个Arduino,想用网页控制它亮个灯、读个传感器数据,以前是不是得装个软件、搞个驱动,麻烦得要死?现在有了Web Serial API,直接在浏览器里就能搞定!

Web Serial API 允许 Web 应用程序通过串口与设备进行通信。这对于各种应用场景都非常有用,比如:

  • 硬件调试: 直接从浏览器读取硬件设备的调试信息。
  • 物联网(IoT): 控制和监控连接到串口的物联网设备。
  • 机器人: 与机器人控制器进行通信。
  • 科学仪器: 从科学仪器收集数据。
  • DIY项目: 各种基于Arduino、树莓派的DIY项目。

二、 浏览器兼容性:

目前,Web Serial API 的兼容性还不是很好。它主要在基于 Chromium 的浏览器(比如 Chrome、Edge)上支持。在使用之前,最好先检查一下浏览器的版本和特性支持情况。

你可以通过以下代码来检测浏览器是否支持 Web Serial API:

if ("serial" in navigator) {
  console.log("Web Serial API is supported!");
} else {
  console.log("Web Serial API is NOT supported!");
}

三、 核心概念和流程

要用Web Serial API,大概需要经历这么几个步骤:

  1. 请求串口访问权限: 相当于跟用户说:“我想用一下你的串口,行不行?”
  2. 连接串口: 拿到授权之后,就可以跟串口建立连接了。
  3. 读写数据: 连接建立好了,就可以愉快地发送和接收数据了。
  4. 关闭串口: 用完了记得关掉,好习惯!

四、 代码实战:从0到1

咱们用一个简单的例子来演示一下,假设我们要控制一个Arduino上的LED灯。

4.1 获取串口访问权限

async function connectSerial() {
  try {
    // 请求串口端口
    const port = await navigator.serial.requestPort();

    // 打开串口,设置波特率等参数
    await port.open({ baudRate: 9600 });

    console.log("Serial port opened successfully!");
    return port; // 返回 port 对象,方便后续操作

  } catch (error) {
    console.error("Error opening serial port:", error);
    return null; // 返回 null,表示连接失败
  }
}

// 在按钮点击事件中调用 connectSerial
document.getElementById("connectButton").addEventListener("click", connectSerial);

这段代码做了什么?

  • navigator.serial.requestPort():这个函数会弹出一个窗口,让用户选择要连接的串口。
  • port.open({ baudRate: 9600 }):打开串口,并设置波特率为9600。这个波特率要和Arduino上的代码保持一致。

4.2 发送数据

// 假设 port 是之前连接成功的 port 对象
async function sendData(port, data) {
  if (!port) {
    console.warn("Serial port is not connected.");
    return;
  }

  const encoder = new TextEncoderStream();
  const writer = encoder.writable.getWriter();

  encoder.readable.pipeTo(port.writable);

  try {
    await writer.write(data);
    console.log(`Sent: ${data}`);
  } catch (error) {
    console.error("Error writing to serial port:", error);
  } finally {
    writer.releaseLock(); // 释放锁
  }
}

// 在按钮点击事件中调用 sendData
document.getElementById("sendButton").addEventListener("click", async () => {
    const port = await connectSerial();
    if(port){
        sendData(port, "1"); // 发送 "1" 打开 LED
    }
});

这段代码做了什么?

  • new TextEncoderStream():创建一个文本编码器,用于将字符串转换为 Uint8Array
  • encoder.readable.pipeTo(port.writable):将编码器的输出连接到串口的输出。
  • writer.write(data):将数据写入串口。
  • writer.releaseLock():释放锁,允许其他操作使用 writer。

4.3 接收数据

// 假设 port 是之前连接成功的 port 对象
async function receiveData(port) {
    if (!port) {
        console.warn("Serial port is not connected.");
        return;
    }

    const decoder = new TextDecoderStream();
    port.readable.pipeTo(decoder.writable);

    const reader = decoder.readable.getReader();

    try {
        while (true) {
            const { value, done } = await reader.read();
            if (done) {
                console.log("Reader done!");
                break;
            }
            console.log(`Received: ${value}`);
        }
    } catch (error) {
        console.error("Error reading from serial port:", error);
    } finally {
        reader.releaseLock();
    }
}

// 在按钮点击事件中调用 receiveData
document.getElementById("receiveButton").addEventListener("click", async () => {
    const port = await connectSerial();
    if(port){
        receiveData(port);
    }

});

这段代码做了什么?

  • new TextDecoderStream():创建一个文本解码器,用于将 Uint8Array 转换为字符串。
  • port.readable.pipeTo(decoder.writable):将串口的输入连接到解码器的输入。
  • reader.read():从串口读取数据。
  • reader.releaseLock():释放锁,允许其他操作使用 reader。

4.4 关闭串口

async function closeSerial(port) {
    if (!port) {
        console.warn("Serial port is not connected.");
        return;
    }

    try {
        await port.close();
        console.log("Serial port closed successfully!");
    } catch (error) {
        console.error("Error closing serial port:", error);
    }
}

// 在按钮点击事件中调用 closeSerial
document.getElementById("closeButton").addEventListener("click", async () => {
    const port = await connectSerial();
    if(port){
        closeSerial(port);
    }
});

4.5 Arduino代码

别忘了,Arduino那边也要写代码,才能配合我们的网页。

const int ledPin = 13;

void setup() {
  Serial.begin(9600);
  pinMode(ledPin, OUTPUT);
}

void loop() {
  if (Serial.available() > 0) {
    char command = Serial.read();
    if (command == '1') {
      digitalWrite(ledPin, HIGH); // 打开LED
      Serial.println("LED ON");
    } else if (command == '0') {
      digitalWrite(ledPin, LOW);  // 关闭LED
      Serial.println("LED OFF");
    }
  }
}

这段代码很简单:

  • Serial.begin(9600):设置串口波特率为9600,要和网页上的代码一致。
  • digitalWrite(ledPin, HIGH/LOW):控制LED的亮灭。

五、 错误处理和最佳实践

  • 权限错误: 用户可能拒绝授予串口访问权限,这时候要做好提示。
  • 串口未找到: 确保串口设备正确连接,并且驱动安装正确。
  • 数据格式错误: 确保发送和接收的数据格式一致。
  • 波特率不匹配: 确保网页和串口设备的波特率设置一致。
  • 异步操作: Web Serial API 是异步的,要使用 async/await 来处理异步操作。
  • try…catch: 使用 try...catch 块来捕获错误,并进行适当的处理。
  • 释放锁: 在完成读写操作后,一定要释放锁,避免阻塞其他操作。

六、 一些高级用法

  • 数据流: 可以使用 ReadableStreamWritableStream 来处理大量数据。
  • 自定义协议: 可以根据自己的需求定义串口通信协议。
  • 事件监听: 可以监听串口的连接和断开事件。

七、 总结

Web Serial API 就像一把瑞士军刀,功能强大,但需要好好学习才能掌握。希望今天的讲解能帮你打开Web Serial API的大门,开启你的硬件Hack之旅!

表格总结:

功能 JavaScript 代码示例 说明
请求串口访问权限 javascript const port = await navigator.serial.requestPort(); 弹出窗口,让用户选择要连接的串口。
打开串口 javascript await port.open({ baudRate: 9600 }); 打开串口,并设置波特率。
发送数据 javascript const writer = encoder.writable.getWriter(); await writer.write(data); writer.releaseLock(); 创建文本编码器,将数据写入串口。
接收数据 javascript const reader = decoder.readable.getReader(); const { value, done } = await reader.read(); reader.releaseLock(); 创建文本解码器,从串口读取数据。
关闭串口 javascript await port.close(); 关闭串口。
错误处理 javascript try { // ... } catch (error) { console.error("Error:", error); } | 使用 try...catch 块来捕获错误。
检测API支持情况 javascript if ("serial" in navigator) { console.log("Web Serial API is supported!"); } else { console.log("Web Serial API is NOT supported!"); } 检测浏览器是否支持Web Serial API

八、 友情提示

  • 多查阅官方文档:https://wicg.github.io/serial/
  • 多看示例代码:Github上有很多Web Serial API的开源项目,可以参考学习。
  • 多动手实践:光看不练假把式,自己动手写代码才能真正掌握。

好了,今天的分享就到这里。希望大家都能用Web Serial API玩出自己的花样! 感谢各位观看!

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注