对于串口连接,如果只是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