unity与usb通信(pc端)
一、本文介绍在windows环境下unity与usb串口进行通信的代码
web版本的放在下一个文章
注:
1.我的硬件是检测磁阻液位,用到四字节十六进制浮点数,所以这里会直接转换到十进制。
2.我的硬件会返回9字节响应,所以我会限制响应长度,可以进行适当更改
重要:再开始前一定要改为.NET 4.x,或者.NET Framework,因为.NET standard 2.0或者2.1不包括需要用到的using System.IO.Ports;更换路径为File/Build Setting/Player Settings.../Other Settings/Api Compatibility Level
二、脚本
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO.Ports;
using System.Text;
using System.Threading;
using UnityEngine;
public class SerialPortCommunicationManager : MonoBehaviour
{
private static SerialPortCommunicationManager _instance;
public static SerialPortCommunicationManager Instance
{
get
{
if (_instance == null)
{
GameObject obj = new GameObject("SerialPortCommunicationManager");
_instance = obj.AddComponent<SerialPortCommunicationManager>();
DontDestroyOnLoad(obj);
}
return _instance;
}
}
public string portName;// 串口号
public int baudRate;// 波特率
public int dataBit;//数据位
public Parity parity;//校验位
public StopBits stopBit;//停止位
private SerialPort sp = new SerialPort();
private List<byte> receiveBuffer = new List<byte>(); // 用于存储接收到的字节
private const int RESPONSE_LENGTH = 9; // 每组响应的长度(9 个字节)
/// <summary>
/// 打开端口
/// </summary>
public void OpenPort()
{
sp = new SerialPort(portName, baudRate, parity, dataBit, stopBit);
sp.ReadTimeout = 20;
try
{
sp.Open();
Debug.Log("端口已打开");
}
catch (Exception e)
{
Debug.LogError("端口未打开: " + e.Message);
}
}
/// <summary>
/// 关闭端口
/// </summary>
public void ClosePort()
{
try
{
sp.Close();
sp.Dispose();
Debug.Log("端口已关闭");
}
catch (Exception e)
{
Debug.LogError("端口无法关闭: " + e.Message);
}
}
/// <summary>
/// 检查串口是否打开
/// </summary>
public bool IsPortOpen()
{
return sp != null && sp.IsOpen;
}
/// <summary>
/// 发送数据
/// </summary>
public void SendData(byte[] dataStr)
{
if (sp != null && sp.IsOpen)
{
sp.Write(dataStr, 0, dataStr.Length);
Debug.LogWarning("发送成功: " + BitConverter.ToString(dataStr).Replace("-", " "));
}
else
{
Debug.LogError("串口未打开,无法发送数据!");
}
}
/// <summary>
/// 接收端口数据
/// </summary>
public void DataReceiveFun()
{
while (true)
{
if (sp != null && sp.IsOpen)
{
try
{
int bytesToRead = sp.BytesToRead;
if (bytesToRead > 0)
{
byte[] buffer = new byte[bytesToRead];
int bytesRead = sp.Read(buffer, 0, bytesToRead);
if (bytesRead > 0)
{
// 将读取到的数据添加到缓冲区
receiveBuffer.AddRange(buffer);
// 按 9 个字节为单位处理数据
while (receiveBuffer.Count >= RESPONSE_LENGTH)
{
byte[] response = receiveBuffer.GetRange(0, RESPONSE_LENGTH).ToArray();
receiveBuffer.RemoveRange(0, RESPONSE_LENGTH); // 移除已处理的数据
// 打印原始响应数据
string responseHex = BitConverter.ToString(response).Replace("-", " ");
Debug.Log($"收到响应: {responseHex}");
// 解析浮点数数据
if (response.Length == RESPONSE_LENGTH && response[0] == 0x01 && response[1] == 0x04)
{
// 假设从第 3 字节起 4 个字节是 IEEE 754 单精度浮点数
byte[] floatBytes = new byte[4];
Array.Copy(response, 3, floatBytes, 0, 4); // 提取第 3 到第 6 字节
// 将字节转换为浮点数
float floatValue = ConvertHexToFloat(floatBytes);
Debug.Log($"解析得到的浮点数: {floatValue}");
}
else
{
Debug.LogWarning("响应格式错误!");
}
}
}
}
}
catch (Exception e)
{
Debug.LogError($"接收数据异常: {e.GetType()} - {e.Message}");
}
}
Thread.Sleep(10); // 降低 CPU 占用,10 毫秒足够快
}
}
/// <summary>
/// 将 4 个字节转换为 IEEE 754 单精度浮点数
/// </summary>
private float ConvertHexToFloat(byte[] bytes)
{
// 确保字节顺序正确(大端或小端取决于你的设备,这里假设是大端)
if (BitConverter.IsLittleEndian)
{
Array.Reverse(bytes); // 如果是小端机器,翻转字节顺序
}
// 直接使用 BitConverter 将字节转换为浮点数
float result = BitConverter.ToSingle(bytes, 0);
return result;
}
// 字符串转字节流 推荐
public byte[] Convert16(string strText)
{
strText = strText.Replace(" ", "");
byte[] bText = new byte[strText.Length / 2];
for (int i = 0; i < strText.Length / 2; i++)
{
bText[i] = Convert.ToByte(Convert.ToInt32(strText.Substring(i * 2, 2), 16));
}
return bText;
}
}
三、使用
using System.Collections;
using System.Collections.Generic;
using System.IO.Ports;
using System.Threading;
using UnityEngine;
public class UseSerialPort : MonoBehaviour
{
private bool isRunning = true; // 控制发送循环的标志
void Start()
{
SerialPortCommunicationManager.Instance.portName="COM3"; // 串口号
SerialPortCommunicationManager.Instance.baudRate=9600; // 波特率
SerialPortCommunicationManager.Instance.dataBit=8; // 数据位
SerialPortCommunicationManager.Instance.parity=Parity.None; // 校验位
SerialPortCommunicationManager.Instance.stopBit=StopBits.One; // 停止位
SerialPortCommunicationManager.Instance.OpenPort(); // 打开串口
// 启动接收线程
new Thread(() =>
{
SerialPortCommunicationManager control = new SerialPortCommunicationManager();
control.DataReceiveFun(); // 持续监听数据
}).Start();
// 启动发送协程,持续发送命令
StartCoroutine(SendCommandRoutine());
}
/// <summary>
/// 持续发送命令
/// </summary>
/// <returns></returns>
IEnumerator SendCommandRoutine()
{
// 这里是我要发送的命令,并且会接收到9字节响应,所以会限制响应长度
string commandHex = "01 04 00 02 00 02 D0 0B"; // 与日志中的命令一致
byte[] commandBytes = SerialPortCommunicationManager.Instance.Convert16(commandHex.Replace(" ", ""));
while (isRunning)
{
SerialPortCommunicationManager.Instance.SendData(commandBytes);
// 等待时间
yield return new WaitForSeconds(0.1f);
}
}
}