使用System.IO.Ports命名空间
此版本的Unity只能使用.net2.0, 但是需要在playersetting 里面把 .net适应调整到.net2.0即可以使用该命名空间。
简单的串口类
using System.IO.Ports;
using System.Threading;
using System.Collections.Generic;
using System;
/// summary
/// 串口通信
///summary
public class SerialPortItem
{
// 开启串口参数
public class SerialPortParam
{
public string PortName;
public int BaudRate;
public Parity Parity;
public StopBits StopBits;
public int DataBits;
public Handshake Handshake;
public bool RtsEnable;
}
public static byte[] outSensorPara;
private SerialPort serialPort;
private bool isClose;
private Thread serialPortThread;//收发串口线程
//读取数据缓存
private byte[] readDataBuffer = new byte[1024];
private int readDataLen;//读取到数据长度
//写数据缓存
private List<byte> writeDataBuffer = new List<byte>();
private bool isOpen;
// 是否开启端口
public bool IsOpen
{
get { return isOpen; }
}
#region 事件
// 接到数据(子线程)
public Action<byte[]> OnReceiveData;
// 错误(子线程)
public Action<string> OnError;
#endregion
// 开启端口
public void Open(SerialPortParam param)
{
serialPort = new SerialPort(param.PortName);
serialPort.BaudRate = param.BaudRate;
serialPort.Parity = param.Parity;
serialPort.StopBits = param.StopBits;
serialPort.DataBits = param.DataBits;
serialPort.Handshake = param.Handshake;
serialPort.RtsEnable = param.RtsEnable;
serialPort.ReadTimeout = 1;
serialPort.WriteTimeout = 1;
try
{
serialPort.Open();
}
catch (Exception ex)
{
if (OnError != null)
OnError(ex.ToString());
Close();
}
isOpen = true;
isClose = false;
serialPortThread = new Thread(new ParameterizedThreadStart(work));
serialPortThread.Start(serialPort);
}
// 发送数据
public void Send(byte[] bytes)
{
if (serialPort != null
&& serialPort.IsOpen)
{
lock (writeDataBuffer)
{
writeDataBuffer.AddRange(bytes);
}
}
}
public void Close()
{
isOpen = false;
isClose = true;
serialPort = null;
}
private void work(object obj)
{
SerialPort sp = (SerialPort)obj;
while (true)
{
if (isClose)
break;
if (!sp.IsOpen)
{
break;
}
//读取
try
{
readDataLen = sp.Read(readDataBuffer, 0, readDataBuffer.Length);
if (readDataLen>0)
{
byte[] receiveBytes = new byte[readDataLen];
Array.Copy(readDataBuffer, 0, receiveBytes, 0, receiveBytes.Length);
outSensorPara = receiveBytes;
if (OnReceiveData != null)
OnReceiveData(receiveBytes);
}
}
catch (TimeoutException)
{//正常结束
}
catch (Exception ex)
{
sp.Close();
if (OnError != null)
OnError(ex.ToString());
}
//写入
if (writeDataBuffer.Count> 0)
{
byte[] writeBytes = null;
lock (writeDataBuffer)
{
writeBytes = writeDataBuffer.ToArray();
writeDataBuffer.Clear();
}
try
{
sp.Write(writeBytes, 0, writeBytes.Length);
}
catch (TimeoutException)
{//正常结束
}
catch (Exception ex)
{
sp.Close();
if (OnError != null)
OnError(ex.ToString());
}
}
}
sp.Close();
}
}
查找串口名称
打开串口需要知道串口名称 ,例如对于USB转串口的设备,在设备管理器中显示的名为USB Serial Port(COMX)
下面是一个根据名称枚举串口设备的类及方法
public class SetupDiWrap
{
public static string ComPortNameFromFriendlyNamePrefix(string friendlyNamePrefix)
{
const string className = "Ports";
Guid[] guids = GetClassGUIDs(className);
System.Text.RegularExpressions.Regex friendlyNameToComPort =
new System.Text.RegularExpressions.Regex(@".? \((COM\d+)\)$"); // "..... (COMxxx)" -> COMxxxx
foreach (Guid guid in guids)
{
// We start at the "root" of the device tree and look for all
// devices that match the interface GUID of a disk
Guid guidClone = guid;
Console.WriteLine(guidClone);
IntPtr h = SetupDiGetClassDevs(ref guidClone, IntPtr.Zero, IntPtr.Zero, DIGCF_PRESENT | DIGCF_PROFILE);
Console.WriteLine(h + " h");
if (h.ToInt64() != INVALID_HANDLE_VALUE)
{
int nDevice = 0;
while (true)
{
SP_DEVINFO_DATA da = new SP_DEVINFO_DATA();
da.cbSize = (uint)Marshal.SizeOf(da);
if (0 == SetupDiEnumDeviceInfo(h, nDevice++, ref da))
{
Console.WriteLine("error");
break;
}
uint RegType;
byte[] ptrBuf = new byte[BUFFER_SIZE];
uint RequiredSize;
if (SetupDiGetDeviceRegistryProperty(h, ref da,
(uint)SPDRP.FRIENDLYNAME, out RegType, ptrBuf,
BUFFER_SIZE, out RequiredSize))
{
const int utf16terminatorSize_bytes = 2;
string friendlyName = System.Text.UnicodeEncoding.Unicode.GetString(ptrBuf, 0, (int)RequiredSize - utf16terminatorSize_bytes);
Console.WriteLine(friendlyName);
if (!friendlyName.StartsWith(friendlyNamePrefix))
continue;
if (!friendlyNameToComPort.IsMatch(friendlyName))
continue;
return friendlyNameToComPort.Match(friendlyName).Groups[1].Value;
}
} // devices
//SetupDiDestroyDeviceInfoList(h);
}
} // class guids
return null;
}
/// <summary>
/// The SP_DEVINFO_DATA structure defines a device instance that is a member of a device information set.
/// </summary>
[StructLayout(LayoutKind.Sequential)]
private struct SP_DEVINFO_DATA
{
/// <summary>Size of the structure, in bytes.</summary>
public uint cbSize;
/// <summary>GUID of the device interface class.</summary>
public Guid ClassGuid;
/// <summary>Handle to this device instance.</summary>
public uint DevInst;
/// <summary>Reserved; do not use.</summary>
public uint Reserved;
}
const int BUFFER_SIZE = 1024;
private enum SPDRP
{
FRIENDLYNAME = 0x0000000C,
}
[DllImport("setupapi.dll", SetLastError = true)]
static extern bool SetupDiClassGuidsFromName(string ClassName,
ref Guid ClassGuidArray1stItem, UInt32 ClassGuidArraySize,
out UInt32 RequiredSize);
[DllImport("setupapi.dll")]
private static extern Int32 SetupDiEnumDeviceInfo(IntPtr DeviceInfoSet,
Int32 MemberIndex, ref SP_DEVINFO_DATA DeviceInterfaceData);
//p
[DllImport("setupapi.dll", CharSet = CharSet.Auto)]
static extern IntPtr SetupDiGetClassDevs( // 1st form using a ClassGUID only, with null Enumerator
ref Guid ClassGuid,
IntPtr Enumerator,
IntPtr hwndParent,
int Flags
);
/// <summary>
/// The SetupDiGetDeviceRegistryProperty function retrieves the specified device property.
/// This handle is typically returned by the SetupDiGetClassDevs or SetupDiGetClassDevsEx function.
/// </summary>
/// <param Name="DeviceInfoSet">Handle to the device information set that contains the interface and its underlying device.</param>
/// <param Name="DeviceInfoData">Pointer to an SP_DEVINFO_DATA structure that defines the device instance.</param>
/// <param Name="Property">Device property to be retrieved. SEE MSDN</param>
/// <param Name="PropertyRegDataType">Pointer to a variable that receives the registry data Type. This parameter can be NULL.</param>
/// <param Name="PropertyBuffer">Pointer to a buffer that receives the requested device property.</param>
/// <param Name="PropertyBufferSize">Size of the buffer, in bytes.</param>
/// <param Name="RequiredSize">Pointer to a variable that receives the required buffer size, in bytes. This parameter can be NULL.</param>
/// <returns>If the function succeeds, the return value is nonzero.</returns>
[DllImport("setupapi.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern bool SetupDiGetDeviceRegistryProperty(
IntPtr DeviceInfoSet,
ref SP_DEVINFO_DATA DeviceInfoData,
uint Property,
out UInt32 PropertyRegDataType,
byte[] PropertyBuffer,
uint PropertyBufferSize,
out UInt32 RequiredSize);
const int DIGCF_DEFAULT = 0x1;
const int DIGCF_PRESENT = 0x2;
const int DIGCF_ALLCLASSES = 0x4;
const int DIGCF_PROFILE = 0x8;
const int DIGCF_DEVICEINTERFACE = 0x10;
const int INVALID_HANDLE_VALUE = -1;
private static Guid[] GetClassGUIDs(string className)
{
UInt32 requiredSize = 0;
Guid[] guidArray = new Guid[1];
bool status = SetupDiClassGuidsFromName(className, ref guidArray[0], 1, out requiredSize);
if (true == status)
{
if (1 < requiredSize)
{
guidArray = new Guid[requiredSize];
SetupDiClassGuidsFromName(className, ref guidArray[0], requiredSize, out requiredSize);
}
}
else
throw new System.ComponentModel.Win32Exception();
return guidArray;
}
}
需要引用系统dll,但是该方法直接在Unity playmode下无法实现,经过多次测试发现,使用x86架构Build出来的exe程序可以实现该方法。不完美的解决了这个问题。