从客户端Web浏览器与串口通信。

13
在我的Web应用程序(sencha extjs 5)中,我有一个用户需求,需要读/写客户端PC串口数据。
我知道客户端浏览器无法访问本地机器硬件,除非在本地机器上安装了一些二进制文件(本地应用程序、Windows服务等)。
我已经看到同样的问题在几年前在stackoverflow论坛上被讨论过。但是我需要知道使用现有技术的最佳方法是什么?

3
请注意,Chrome应用程序将于2018年停用。更多信息请参见:https://developer.chrome.com/apps/migration - Richard de Ree
请查看Web Serial API(示例在此处)。还可以参考浏览器支持表格 - djvg
相关链接:https://dev59.com/DU_Sa4cB1Zd3GeqP-0Jw,https://dev59.com/WGUp5IYBdhLWcg3wmIYt - djvg
3个回答

14
使用Web串行API。 我使用它仅从我的带RS232串行接口的体重秤中读取数据。
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Web Serial</title>
</head>
<body>

  <div class="serial-scale-div">
        <button class="btn" id="connect-to-serial">Connect with Serial Device</button>
  </div>

  <button id="get-serial-messages">Get serial messages</button>
  
  <div id="serial-messages-container">
    <div class="message"></div>
  </div>

  <script>
    "use strict";
    class SerialScaleController {
        constructor() {
            this.encoder = new TextEncoder();
            this.decoder = new TextDecoder();
        }
        async init() {
            if ('serial' in navigator) {
                try {
                    const port = await navigator.serial.requestPort();
                    await port.open({ baudRate: 9600 });
                    this.reader = port.readable.getReader();
                    let signals = await port.getSignals();
                    console.log(signals);
                }
                catch (err) {
                    console.error('There was an error opening the serial port:', err);
                }
            }
            else {
                console.error('Web serial doesn\'t seem to be enabled in your browser. Try enabling it by visiting:');
                console.error('chrome://flags/#enable-experimental-web-platform-features');
                console.error('opera://flags/#enable-experimental-web-platform-features');
                console.error('edge://flags/#enable-experimental-web-platform-features');
            }
        }
        async read() {
            try {
                const readerData = await this.reader.read();
                console.log(readerData)
                return this.decoder.decode(readerData.value);
            }
            catch (err) {
                const errorMessage = `error reading data: ${err}`;
                console.error(errorMessage);
                return errorMessage;
            }
        }
    }

    const serialScaleController = new SerialScaleController();
    const connect = document.getElementById('connect-to-serial');
    const getSerialMessages = document.getElementById('get-serial-messages');

    connect.addEventListener('pointerdown', () => {
      serialScaleController.init();
    });

    getSerialMessages.addEventListener('pointerdown', async () => {
      getSerialMessage();
    });

    async function getSerialMessage() {
      document.querySelector("#serial-messages-container .message").innerText += await serialScaleController.read()
    }

  </script>
</body>
</html>

查看此演示此代码,以获取更详细的示例。

您可能需要在浏览器上启用Serial API功能。 以下是引用摘录

正如您所想象的那样,此API目前只受到现代基于Chromium的桌面浏览器的支持(2020年4月),但希望在不久的将来得到改善。目前,您需要启用浏览器的实验性Web平台功能,只需复制并粘贴正确的URL:

chrome://flags/#enable-experimental-web-platform-features opera://flags/#enable-experimental-web-platform-features edge://flags/#enable-experimental-web-platform-features

参考文献:

https://dev.to/unjavascripter/the-amazing-powers-of-the-web-web-serial-api-3ilc

https://github.com/UnJavaScripter/web-serial-example


这是正确的答案。它是最新的,并且在Chrome上没有问题。迫不及待地希望它能默认启用。 - save_jeff

9

好的,一种方法是开发一个Chrome应用程序。您可以使用chrome.serial API。

https://developer.chrome.com/apps/serial

示例代码,

在你的 manifest.json 文件中,

{
  "name": "Serial Sample",
  "description": "Read/Write from/to serial port.",
  "version": "1.0",
  "manifest_version": 2,
  "permissions": ["serial"],
  "app": {
    "background": {
      "scripts": ["background.js"]
    }
  }
}

在你的background.js文件中,
const DEVICE_PATH = 'COM1';
const serial = chrome.serial;
var dataRecieved="";

/* Interprets an ArrayBuffer as UTF-8 encoded string data. */
var ab2str = function(buf) {
    var bufView = new Uint8Array(buf);
    var encodedString = String.fromCharCode.apply(null, bufView);
    return decodeURIComponent(escape(encodedString));
};

/* Converts a string to UTF-8 encoding in a Uint8Array; returns the array buffer. */
var str2ab = function(str) {
    var encodedString = unescape(encodeURIComponent(str));
    var bytes = new Uint8Array(encodedString.length);
    for (var i = 0; i < encodedString.length; ++i) {
        bytes[i] = encodedString.charCodeAt(i);
    }
    return bytes.buffer;
};


var SerialConnection = function() {
    this.connectionId = -1;
    this.lineBuffer = "";
    this.boundOnReceive = this.onReceive.bind(this);
    this.boundOnReceiveError = this.onReceiveError.bind(this);
    this.onConnect = new chrome.Event();
    this.onReadLine = new chrome.Event();
    this.onError = new chrome.Event();
};

SerialConnection.prototype.onConnectComplete = function(connectionInfo) {
    if (!connectionInfo) {
        log("Connection failed.");
        return;
    }
    this.connectionId = connectionInfo.connectionId;
    chrome.serial.onReceive.addListener(this.boundOnReceive);
    chrome.serial.onReceiveError.addListener(this.boundOnReceiveError);
    this.onConnect.dispatch();
};

SerialConnection.prototype.onReceive = function(receiveInfo) {
    if (receiveInfo.connectionId !== this.connectionId) {
        return;
    }

    this.lineBuffer += ab2str(receiveInfo.data);

    var index;
    while ((index = this.lineBuffer.indexOf('\n')) >= 0) {
        var line = this.lineBuffer.substr(0, index + 1);
        this.onReadLine.dispatch(line);
        this.lineBuffer = this.lineBuffer.substr(index + 1);
    }
};

SerialConnection.prototype.onReceiveError = function(errorInfo) {
    if (errorInfo.connectionId === this.connectionId) {
        this.onError.dispatch(errorInfo.error);
    }
};

SerialConnection.prototype.connect = function(path) {
    serial.connect(path, this.onConnectComplete.bind(this))
};

SerialConnection.prototype.send = function(msg) {
    if (this.connectionId < 0) {
        throw 'Invalid connection';
    }
    serial.send(this.connectionId, str2ab(msg), function() {});
};

SerialConnection.prototype.disconnect = function() {
    if (this.connectionId < 0) {
        throw 'Invalid connection';
    }
    serial.disconnect(this.connectionId, function() {});
};


var connection = new SerialConnection();

connection.onConnect.addListener(function() {
    //console.log('connected to: ' + DEVICE_PATH);
});

connection.onReadLine.addListener(function (line) {
    //Serial port data recieve event.
    dataRecieved = dataRecieved +line;
});

connection.connect(DEVICE_PATH);

一旦您创建了与串口通信的Chrome应用程序,下一步是允许您的外部网页使用JavaScript与Chrome应用程序通信。
为此,在您的manifest.json文件中添加以下内容:
"externally_connectable": {
"matches": ["*://*.example.com/*"]
}

这将允许位于您的example.com域上的外部网页与您的Chrome应用程序进行通信。
在您的网页中,
    // The ID of the extension we want to talk to.
    var editorExtensionId = "nboladondmajlaalmcdupihoilpcketyl";

   // Make a simple request:
   chrome.runtime.sendMessage(editorExtensionId, 
   { data: "data to pass to the chrome app" },  
   function (response)
   {
    alert(response);
   });

在你的 Chrome 应用中,
chrome.runtime.onMessageExternal.addListener(
  function (request, sender, sendResponse) {
        sendResponse("Send serial port data to the web page");
  });

https://developer.chrome.com/apps/messaging


3

兄弟,那太疯狂了...你能用React实现吗? - Elias
请随意创建一个React版本。它包括JS、HTML和CSS不到200行代码。 - mmiscool

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接