Unity5.6.1 串列埠通訊親測可行解決方案
使用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 { /// <summary> /// 開啟串列埠引數 /// </summary> 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; /// <summary> /// 是否開啟埠 /// </summary> public bool IsOpen { get { return isOpen; } } #region 事件 /// <summary> /// 接到資料(子執行緒) /// </summary> public Action<byte[]> OnReceiveData; /// <summary> /// 錯誤(子執行緒) /// </summary> public Action<string> OnError; #endregion /// <summary> /// 開啟埠 /// </summary> /// <param name="param"></param> 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); } /// <summary> /// 傳送資料 /// </summary> /// <param name="bytes"></param> 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程式可以實現該方法。不完美的解決了這個問題。