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

各位靓仔靓女们,早上好/下午好/晚上好!我是今天的主讲人,咱们今天要聊聊Web USB API,这玩意儿能让你在浏览器里直接跟USB设备“谈恋爱”,是不是听起来就有点小激动? 别怕,其实没那么玄乎,咱们争取用大白话把它说明白。

一、啥是Web USB API?(这名字听着就高大上)

简单说,Web USB API就是浏览器提供的一套接口,让你的网站(准确说是Web应用)可以直接访问连在电脑上的USB设备。以前这事儿只能客户端程序干,现在浏览器也能插一脚了,是不是感觉世界都美好了?

  • 为啥要用它?

    • 免驱动安装: 很多USB设备都需要安装驱动才能用,Web USB API可以让用户直接在浏览器里用,省去了安装驱动的麻烦。
    • 跨平台: 只要浏览器支持,你的Web应用就能在各种操作系统上访问USB设备,不用为不同平台写不同的代码。
    • 安全性: 浏览器会控制Web应用对USB设备的访问权限,避免恶意网站搞破坏。
    • 便捷性: 用户插上USB设备,打开你的网站,就能直接使用,简直不要太方便!
  • 能干啥?

    • 硬件调试: 直接在浏览器里调试你的硬件设备,比如单片机、传感器等等。
    • 自定义HID设备: 控制各种奇奇怪怪的HID设备,比如游戏手柄、键盘、鼠标等等。
    • 数据采集: 从USB设备上采集数据,比如温度、湿度、压力等等。
    • 固件升级: 直接在浏览器里给你的USB设备升级固件。
    • 打印机/扫描仪: 理论上,只要设备支持,就能在浏览器里控制打印机/扫描仪。

二、怎么用?(代码才是王道!)

想跟USB设备“谈恋爱”,总得先认识一下吧?下面咱们就一步一步来,看看怎么用Web USB API。

  1. 获取USB设备列表

    首先,你需要让用户选择要连接的USB设备。这可以通过navigator.usb.requestDevice()方法来实现。

    async function connectUSB() {
      try {
        const device = await navigator.usb.requestDevice({
          filters: [
            { vendorId: 0x2341, productId: 0x8036 }, // Arduino Uno
            { vendorId: 0x046d, productId: 0xc52b }  // Logitech Mouse
          ]
        });
        console.log("USB Device selected:", device);
        // 连接设备
        await device.open();
        console.log("Device opened successfully.");
        //选择 configuration
        if(device.configuration === null){
            await device.selectConfiguration(1);
        }
        // Claim interface
        await device.claimInterface(0);
        console.log("Interface claimed successfully.");
        //接下来就可以进行数据传输了
        //...
      } catch (error) {
        console.error("Error connecting to USB device:", error);
      }
    }
    • navigator.usb.requestDevice():会弹出一个窗口,让用户选择USB设备。
    • filters:是一个数组,用于过滤USB设备。每个对象包含vendorId(厂商ID)和productId(产品ID)。这两个ID可以在设备的硬件信息里找到。
    • vendorIdproductId:一定要填对,不然用户可能选不到你的设备。
    • await:因为requestDevice()是个异步函数,所以要用await等待它执行完成。
    • device.open(): 打开设备,准备进行通信。
    • device.selectConfiguration(1): 选择配置。通常情况下,USB设备只有一个配置(configuration 1),但有些设备可能有多个配置。选择正确的配置很重要,否则可能无法正常通信。
    • device.claimInterface(0): 声明接口。USB设备通常有多个接口(interface),每个接口负责不同的功能。你需要声明你要使用的接口,才能进行数据传输。通常情况下,接口0是主要的接口。
  2. 打开USB设备并声明接口

    拿到设备对象后,就可以打开设备并声明要使用的接口了。

    //假设device是上面获取到的设备对象
    
  3. 数据传输

    打开设备并声明接口后,就可以进行数据传输了。Web USB API提供了transferIn()transferOut()方法,用于接收和发送数据。

    • 发送数据(transferOut)

      async function sendData(device, endpoint, data) {
        try {
          const result = await device.transferOut(endpoint, data);
          console.log("Data sent:", result);
        } catch (error) {
          console.error("Error sending data:", error);
        }
      }
      
      // 举例:向endpoint 1 发送数据
      const data = new Uint8Array([0x01, 0x02, 0x03, 0x04]);
      sendData(device, 1, data);
      • endpoint:是USB端点的地址。每个USB接口都有若干个端点,用于数据传输。你需要知道你要使用的端点的地址。一般来说,端点地址是1到127之间的整数。
      • data:是要发送的数据,必须是一个Uint8Array对象。
      • result:是一个对象,包含了传输的结果信息,比如传输的字节数。
    • 接收数据(transferIn)

      async function receiveData(device, endpoint, length) {
        try {
          const result = await device.transferIn(endpoint, length);
          console.log("Data received:", result.data);
          return result.data;
        } catch (error) {
          console.error("Error receiving data:", error);
          return null;
        }
      }
      
      // 举例:从endpoint 2 接收64字节的数据
      const receivedData = await receiveData(device, 2, 64);
      if (receivedData) {
        // 处理接收到的数据
        const decoder = new TextDecoder();
        const text = decoder.decode(receivedData);
        console.log("Received text:", text);
      }
      • length:是要接收的数据的长度,单位是字节。
      • result.data:是一个DataView对象,包含了接收到的数据。
      • TextDecoder:用于将DataView对象转换为字符串。
  4. 关闭USB设备

    用完USB设备后,一定要记得关闭它,释放资源。

    async function disconnectUSB(device) {
      try {
        await device.releaseInterface(0); //释放接口
        await device.close();
        console.log("USB Device disconnected.");
      } catch (error) {
        console.error("Error disconnecting USB device:", error);
      }
    }
    
    // 举例:
    disconnectUSB(device);
    • device.releaseInterface(0):释放之前声明的接口。
    • device.close():关闭设备。

三、实战演练(来点真家伙!)

光说不练假把式,咱们来个小例子,用Web USB API控制一个Arduino Uno开发板上的LED灯。

  1. 硬件准备

    • Arduino Uno开发板
    • LED灯
    • 220欧姆电阻
    • 杜邦线

    把LED灯的正极通过220欧姆电阻连接到Arduino Uno的数字引脚13,LED灯的负极连接到GND。

  2. Arduino代码

    void setup() {
      pinMode(13, OUTPUT); // 设置数字引脚13为输出模式
      Serial.begin(115200); // 初始化串口通信
    }
    
    void loop() {
      if (Serial.available() > 0) {
        int command = Serial.read(); // 读取串口数据
        if (command == '1') {
          digitalWrite(13, HIGH); // 点亮LED灯
          Serial.println("LED ON");
        } else if (command == '0') {
          digitalWrite(13, LOW);  // 关闭LED灯
          Serial.println("LED OFF");
        }
      }
    }

    把这段代码烧录到你的Arduino Uno开发板上。

  3. Web页面代码

    <!DOCTYPE html>
    <html>
    <head>
      <title>Web USB Arduino LED Control</title>
    </head>
    <body>
      <h1>Web USB Arduino LED Control</h1>
      <button id="connectButton">Connect USB</button>
      <button id="ledOnButton" disabled>LED ON</button>
      <button id="ledOffButton" disabled>LED OFF</button>
      <script>
        const connectButton = document.getElementById("connectButton");
        const ledOnButton = document.getElementById("ledOnButton");
        const ledOffButton = document.getElementById("ledOffButton");
        let device = null;
    
        connectButton.addEventListener("click", async () => {
          try {
            device = await navigator.usb.requestDevice({
              filters: [{ vendorId: 0x2341, productId: 0x8036 }], // Arduino Uno
            });
    
            console.log("USB Device selected:", device);
            await device.open();
            console.log("Device opened successfully.");
    
            if(device.configuration === null){
                await device.selectConfiguration(1);
            }
            await device.claimInterface(0);
            console.log("Interface claimed successfully.");
    
            connectButton.disabled = true;
            ledOnButton.disabled = false;
            ledOffButton.disabled = false;
    
          } catch (error) {
            console.error("Error connecting to USB device:", error);
          }
        });
    
        ledOnButton.addEventListener("click", async () => {
          await sendData(device, 1, new Uint8Array([0x31])); // 发送 '1'
        });
    
        ledOffButton.addEventListener("click", async () => {
          await sendData(device, 1, new Uint8Array([0x30])); // 发送 '0'
        });
    
        async function sendData(device, endpoint, data) {
          try {
            const result = await device.transferOut(endpoint, data);
            console.log("Data sent:", result);
          } catch (error) {
            console.error("Error sending data:", error);
          }
        }
      </script>
    </body>
    </html>

    把这段代码保存为一个HTML文件,用浏览器打开。

  4. 操作步骤

    1. 把Arduino Uno开发板连接到电脑上。
    2. 点击Web页面上的"Connect USB"按钮,浏览器会弹出一个窗口,让你选择Arduino Uno设备。
    3. 选择Arduino Uno设备,点击"连接"。
    4. 如果连接成功,"LED ON"和"LED OFF"按钮会变成可用状态。
    5. 点击"LED ON"按钮,Arduino Uno上的LED灯会被点亮。
    6. 点击"LED OFF"按钮,Arduino Uno上的LED灯会被关闭。

四、注意事项(雷区警告!)

使用Web USB API的时候,要注意以下几点:

  • HTTPS: Web USB API只能在HTTPS协议下使用,因为涉及到安全问题。
  • 用户授权: 每次连接USB设备都需要用户授权,不能偷偷摸摸地连接。
  • 设备兼容性: 并不是所有的USB设备都支持Web USB API,需要设备厂商的支持。
  • 浏览器兼容性: 目前只有Chrome、Edge等少数浏览器支持Web USB API。
  • 权限问题: 一些操作系统可能会限制Web应用对USB设备的访问权限。
  • 错误处理: Web USB API可能会抛出各种异常,一定要做好错误处理。

五、常见问题(排坑指南!)

  • Q:为什么我的设备没有出现在设备选择窗口里?

    • A:检查你的vendorIdproductId是否正确。
    • A:检查你的设备是否支持Web USB API。
    • A:检查你的浏览器是否支持Web USB API。
  • Q:为什么我连接设备后无法发送或接收数据?

    • A:检查你是否正确地打开了设备并声明了接口。
    • A:检查你的endpoint是否正确。
    • A:检查你的数据格式是否正确。
  • Q:为什么我的代码在Chrome里能用,在Edge里不能用?

    • A:不同的浏览器对Web USB API的实现可能略有差异,需要做一些兼容性处理。
    • A:检查你的浏览器版本是否过低。

六、一些建议(老司机经验!)

  • 多看文档: Web USB API的官方文档是最好的学习资料。
  • 多看例子: 网上有很多Web USB API的例子,可以参考一下。
  • 多调试: 调试是解决问题的最好方法。
  • 多交流: 遇到问题可以到社区里提问,大家一起讨论。

七、Web Serial API和Web Bluetooth API (拓展)

除了Web USB API,浏览器还提供了Web Serial API和Web Bluetooth API,用于与串口设备和蓝牙设备进行通信。

  • Web Serial API 允许网站通过串口与设备通信,例如与微控制器、3D打印机等连接。
  • Web Bluetooth API 允许网站与蓝牙设备进行通信,例如与心率监测器、智能家居设备等连接。

它们与Web USB API类似,都是为了让Web应用能够直接与硬件设备进行交互。

八、总结

Web USB API是一个强大的工具,可以让你在浏览器里直接跟USB设备“谈恋爱”。虽然有一些坑,但是只要你掌握了正确的方法,就能轻松驾驭它。希望今天的讲座能帮助你入门Web USB API,开启你的硬件探索之旅! 祝各位玩得开心!

API 名称 功能 适用场景
Web USB API 与 USB 设备直接通信 硬件调试、自定义 HID 设备、数据采集、固件升级、打印机/扫描仪等
Web Serial API 通过串口与设备通信 与微控制器、3D 打印机等连接
Web Bluetooth API 与蓝牙设备通信 与心率监测器、智能家居设备等连接
安全性 需要 HTTPS 协议和用户授权 所有 API 都需要在 HTTPS 协议下运行,并需要用户授权才能访问设备
浏览器兼容性 Chrome、Edge 等少数浏览器支持 Chrome、Edge 等浏览器支持,但不同浏览器对 API 的实现可能略有差异
权限管理 操作系统可能限制 Web 应用对 USB 设备的访问权限 操作系统和浏览器都会对 Web 应用访问硬件设备进行权限管理,例如需要用户授权、限制访问频率等
错误处理 需要做好错误处理 所有 API 在使用过程中都可能抛出异常,例如设备未连接、权限不足等,需要做好错误处理
学习资源 官方文档、示例代码、社区论坛 官方文档是最好的学习资料,网上有很多示例代码可以参考,社区论坛可以交流问题
使用建议 多看文档、多看例子、多调试、多交流 充分利用各种学习资源,遇到问题及时调试和交流

发表回复

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