前言

引入

本人的大一年度项目设计的网页中有串口通信的需求,在初步设计时非常想当然的认为这是可以在后端实现的,编写程序时突然反应过来,后端在服务器上怎么与串口通信(QAO)。多次与同伴,指导老师讨论无果,某天突然柳暗花明在Github发现了一个项目:Curtion/Web-SerialPort

项目在线体验:https://curtion.github.io/Web-SerialPort/

网页如图:

在这里我接了Arduino试验(烧了一个小程序进去,收到“1”LED就会闪烁),发现可行。

实现原理

怎么实现的呢?由这个项目发现了一个可以从前端与串口通信的Api:串行 - Web API 接口

Api文档介绍说:

Web 串行 API 为网站提供了一种从串行设备读取和写入串行设备的方法。这些设备可以通过串行端口连接,也可以是模拟串行端口的 USB 或蓝牙设备。

研究了一下确定可行就开始试验(Api有一些缺陷导致某些浏览器对某些方法不兼容,但是交项目是够用了🥲,Api后续大概也会完善兼容性)

代码实现

介绍

整理了一下需求,项目主要需要实现两个串口通信功能和两个辅助功能

  • 检测串口设备

  • 发送文本到串口

  • 测试串口通信是否正常

  • 上传txt文件发送到串口

下面编写一个简单的代码实现

(这里测试功能用发送“1”来代替)

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Serial Port Example</title>
    <style>
      body {
        font-family: Arial, sans-serif;
        margin: 50px;
        text-align: center;
      }
      button {
        margin: 10px;
        padding: 10px 20px;
        font-size: 16px;
      }
      #status {
        margin-top: 20px;
        font-size: 18px;
        color: green;
      }
      #error {
        margin-top: 20px;
        font-size: 18px;
        color: red;
      }
      #fileInput {
        display: none;
      }
    </style>
  </head>
  <body>
    <h1>Serial Port Example</h1>
    <button id="detectButton">检测设备</button>
    <button id="sendCommandButton">发送命令 "1"</button>
    <button id="uploadButton">上传TXT文件</button>
    <button id="sendFileButton">发送TXT文件内容</button>

    <input type="file" id="fileInput" accept=".txt">

    <div id="status"></div>
    <div id="error"></div>

    <script>
      let port;
      let fileContent = '';

      document.getElementById('detectButton').addEventListener('click', async () => {
        try {
          port = await navigator.serial.requestPort();
          await port.open({ baudRate: 9600 });
            showStatus("成功啦");
            } catch (err) {
            showError("抱歉失败了", err);
            }
            });

            document.getElementById('sendCommandButton').addEventListener('click', async () => {
            try {
            if (!port) {
            throw new Error("没有检测到设备");
            }
            const writer = port.writable.getWriter();
            await writer.write(new TextEncoder().encode('1'));
            writer.releaseLock();
            showStatus("成功啦");
            } catch (err) {
            showError("抱歉失败了", err);
            }
            });

            document.getElementById('uploadButton').addEventListener('click', () => {
            document.getElementById('fileInput').click();
            });

            document.getElementById('fileInput').addEventListener('change', (event) => {
            const file = event.target.files[0];
            const reader = new FileReader();
            reader.onload = function(e) {
            fileContent = e.target.result;
            showStatus("文件上传成功");
            };
            reader.onerror = function(e) {
            showError("文件读取失败", e);
            };
            reader.readAsText(file);
            });

            document.getElementById('sendFileButton').addEventListener('click', async () => {
            try {
            if (!port) {
            throw new Error("没有检测到设备");
            }
            if (!fileContent) {
            throw new Error("没有上传文件");
            }
            const writer = port.writable.getWriter();
            await writer.write(new TextEncoder().encode(fileContent));
            writer.releaseLock();
            showStatus("成功啦");
            } catch (err) {
            showError("抱歉失败了", err);
            }
            });

            function showStatus(message) {
            document.getElementById('status').textContent = message;
        document.getElementById('error').textContent = '';
    }

    function showError(message, error) {
        document.getElementById('status').textContent = '';
        document.getElementById('error').textContent = `${message}: ${error.message}`;
    }
</script>
</body>
</html>

效果

界面如图(在Edge和Google浏览器上经测试功能正常):