各位观众老爷,大家好!今天咱们来聊聊一个听起来有点神秘,但其实挺接地气的玩意儿: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,大概需要经历这么几个步骤:
- 请求串口访问权限: 相当于跟用户说:“我想用一下你的串口,行不行?”
- 连接串口: 拿到授权之后,就可以跟串口建立连接了。
- 读写数据: 连接建立好了,就可以愉快地发送和接收数据了。
- 关闭串口: 用完了记得关掉,好习惯!
四、 代码实战:从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
块来捕获错误,并进行适当的处理。 - 释放锁: 在完成读写操作后,一定要释放锁,避免阻塞其他操作。
六、 一些高级用法
- 数据流: 可以使用
ReadableStream
和WritableStream
来处理大量数据。 - 自定义协议: 可以根据自己的需求定义串口通信协议。
- 事件监听: 可以监听串口的连接和断开事件。
七、 总结
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玩出自己的花样! 感谢各位观看!