unity Webgl串口连接usb设备

2 篇文章 0 订阅

对于串口连接,如果只是pc端可以使用System.IO.Ports.SerialPort这个类进行连接,但是如果unity要打包到webgl平台就不支持了,这时就需要用新的方式进行连接了。通过查找资料我知道了js使用WebSerialAPI是可以进行usb串口通讯的,但是支持的浏览器比较少,而且必须要用https,但是也基本满足我们的要求了。具体支持的浏览器参考:Web Serial API - Web APIs | MDN。当知道js支持串口通讯后就可以用个unity与js通讯来完成我们的要求,具体使用可以参考:Web Serial API。我们需要先写一个js代码来给unity调用,同时js也可以调用unity的方法类接收回调。首先新建一个后缀名为jslib的js文件来作为c#调用js的桥梁。具体代码如下:

mergeInto(LibraryManager.library, {
    ports: {}, contentedId: null, writer: null, reader: null, objName: null,
    SendData: async function (byteArray, byteArraySize) {
        if (typeof (writer) == "undefined" || writer == null) {
            console.warn("Not connected");
            return;
        }
        var bytes = new Uint8Array(byteArraySize);
        for (var i = 0; i < byteArraySize; i++) {
            bytes[i] = HEAPU8[byteArray + i];
        }
        await writer.write(bytes);
        console.log("Send Message" + bytes);
    },
    CloseSerial: function () {
        if (typeof (reader) == "undefined" || reader == null) {
            console.warn("already closed");
            return;
        }
        reader.cancel();
    },
    OpenSerial: async function (id) {
        var idStr = UTF8ToString(id);
        var port = ports[idStr];
        if (typeof (port) == "undefined" || port == null) {
            console.error("no existent port:" + idStr)
            return;
        }
        if (typeof (contentedId) == "string") {
            console.error("Already connected:" + contentedId)
            return;
        }
        await port.open({ baudRate: 9600 })
        reader = port.readable.getReader();
        writer = port.writable.getWriter();
        contentedId = idStr;
        SendMessage(objName, 'ContentedSerial', idStr);
        try {
            while (true) {
                const { value, done } = await reader.read();
                if (done) {
                    break;
                }
                let arrStr = JSON.stringify(Array.from(value));
                console.log("Received data:" + arrStr);
                SendMessage(objName, 'ReceivedData', arrStr);
            }
        } catch (error) {
            console.error(error)
        } finally {
            reader.releaseLock();
            writer.releaseLock();
            await port.close();
            reader = null; writer = null;
            console.log('SerialClosed');
            SendMessage(objName, 'SerialClosed', idStr);
            contentedId = null;
        }
    },
    RequestPort: async function (name) {
        objName = UTF8ToString(name);
        if ('serial' in navigator) {
            ports = {}; var unknownIndex = 0;                 
            var ps = await navigator.serial.getPorts();
            var getId = function (p) {
                if (typeof (p.id) == "undefined") {
                    var id = p.getInfo().usbVendorId;
                    if (typeof (id) != "undefined" && id != null) {
                        p.id = id.toString(16);
                    } else {//unknown device                      
                        do {
                            id = "unknown" + unknownIndex;
                            unknownIndex++
                        } while (id in ports)
                        p.id = id;
                    }
                }
            };
            for (var i in ps) {
                var p = ps[i];
                getId(p);
                ports[p.id] = p;
            }
            var port = null; 
            try {
                port = await navigator.serial.requestPort();
            }
            catch (e) {
                console.warn(e);
            }
            if (port != null) {
                getId(port);
                ports[port.id] = p;
            }
            var arr = [];
            for (var i in ports) {
                arr.push(i);
            }
            var idStr = JSON.stringify(arr);
            SendMessage(objName, 'GetPorts', idStr);
            if (typeof (contentedId) == "undefined") contentedId = null;
            if (port != null && contentedId == null) {
                await port.open({ baudRate: 9600 })
                reader = port.readable.getReader();
                writer = port.writable.getWriter();
                contentedId = port.id;
                SendMessage(objName, 'ContentedSerial', contentedId);
                try {
                    while (true) {
                        const { value, done } = await reader.read();
                        if (done) {
                            break;
                        }
                        let arrStr = JSON.stringify(Array.from(value));
                        console.log("Received data:" + arrStr);
                        SendMessage(objName, 'ReceivedData', arrStr);
                    }
                } catch (error) {
                    console.error(error);
                } finally {
                    reader.releaseLock();
                    writer.releaseLock();
                    await port.close();
                    reader = null; writer = null;
                    console.log('SerialClosed');
                    SendMessage(objName, 'SerialClosed', contentedId);
                    contentedId = null;
                }
            }
        } else {
            console.error('Not Supported');
        }
    },
});

以上代码写了四个函数给unity调用分别是搜索并请求端口:RequestPort,这里需要unity提供一个参数name来接收js的回调消息。OpenSerial打开串口,提供一个串口id,这个id是js生成传递给unity的。生成的规则为如果串口设备有设备id(usbVendorId)则直接作为id,没有就自己生成一个。具体代码以上都有,函数名为getId。生成规则为:unknown+数字代号,这个可以自己随意命名,只要不重复能区分串口就行了。SendData用来发送串口消息,需要unity传递一个byte数组与数组长度两个参数。CloseSerial为关闭串口的方法。

以上是unity需要调用的js方法。unity也需要接收回调方法,也有四个分别为ContentedSerial,这里会接收到一个参数串口id,SerialClosed这个回调来告诉unity串口已经关闭。GetPorts回调来告诉unity获取了串口id的json数组需要unity解析来显示串口列表。最重要的回调消息是ReceivedData,这里收到的也是json字符串,需要解析成byte数组在进行处理。具体c#调用代码如下:

using System.Collections;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;
using UnityEngine;
using UnityEngine.UI;
using Newtonsoft.Json;

public class WebGlTest : MonoBehaviour
{
    [DllImport("__Internal")]
    public static extern void SendData(byte[] data,int length);
    [DllImport("__Internal")]
    public static extern void CloseSerial();
    [DllImport("__Internal")]
    public static extern void RequestPort(string objName);
    [DllImport("__Internal")]
    public static extern void OpenSerial(string usbVendorId);

    public Dropdown 设备列表;
    string ContentedId = null;
    // Start is called before the first frame update
    void Start()
    {
        
    }
    public void 发送消息()
    {
        byte[] data = Encoding.UTF8.GetBytes("你是大笨蛋!");
        SendData(data, data.Length);
    }
    public void 搜索端口()
    {
        RequestPort(name);
    }

    public void 断开连接()
    {
        CloseSerial();
    }
    public void 连接串口()
    {
        string id = 设备列表.captionText.text;
        OpenSerial(id);       
    }

    public void ContentedSerial(string usbVendorId)
    {        
        Debug.Log("连接到设备" + usbVendorId);
        ContentedId = usbVendorId;
    }
    public void SerialClosed()
    {
        ContentedId = null;
        Debug.Log("串口已关闭");
    }
    public void ReceivedData(string dataStr)
    {        
        byte[] data = JsonConvert.DeserializeObject<byte[]>(dataStr);
        Debug.Log("收到数据" + dataStr+",二进制数据长度:"+data.Length);
        string str = Encoding.UTF8.GetString(data);
        Debug.Log("解析成字符串为:"+str);
    }
    public void GetPorts(string jsonStr)
    {       
        Debug.Log("搜索到了设备:" + jsonStr);
        List<string> arr = JsonConvert.DeserializeObject<List<string>>(jsonStr);
        设备列表.ClearOptions();
        设备列表.AddOptions(arr);
    }
}

通过以上就可以实现webgl简单串口通讯了。需要注意以上必须打包后运行,并且要运行在支持串口通讯的浏览器上,然后按f12查看控制台消息就能看到运行效果。demo下载:https://download.csdn.net/download/chunyu90225/87554740

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值