Unity3D软硬件Unity3D和串口通信 SerialPort类完全教程
大家好,我是佛系工程师☆恬静的小魔龙☆,不定时更新Unity开发技巧,觉得有用记得一键三连哦。
一、前言
最近,有小伙伴再整串口通信,问我有没有写好的串口代码,我一瞅我最近写的都在19年了。
比如:
2017-12-04 写的【Unity3D软硬件】Unity3D 与串口的通信程序的开发,软件硬件结合
2019-09-20 写的【Unity3D软硬件】Unity3d与串口通信程序的开发
实在是有段时间没有搞软硬件通信了,那么这篇文章就总结一下如何让Unity3D与串口的通信。
二、思路整理
要想完美的完成一件事件,首先需要在脑子里将思路进行整理,确定自己的思路。
先说串口通信。
串口通信,首先要扫描那些端口是可用的,只有端口可用才能正常的发送指令。
扫描完端口,获得可用的端口列表,如下图所示:
打开串口需要端口号、波特率、数据位、停止位、校验位
,如下图所示:
打开完串口,之后就是向串口发送信息,发送信息就是串口的API就可以了。
发送信息,肯定就有接收信息,接收信息可以使用线程不停的接收信息,也可以使用回调委托接收。
最后,断开串口。
总结一下就是:
- 扫描端口(试错端口、注册表查看、API查看)
- 连接串口(端口号、波特率、数据位、停止位、校验位)
- 发送信息(API)
- 接收信息(线程、委托)
- 断开串口(API)
三、实现代码
新建项目,使用版本Unity 2019.3.8f1
,模板就用3D就好啦,命名为Demo_SerialProt_2019.3.8
:
首先,引入命名空间using System.IO.Ports;
,如果显示命名空间“System.IO”中不存在类型或命名空间名"Ports",缺少程序集引用错误:
可以在Project Settings面板中,将Api Compatibility Level设置为.NET 4.x:
编译完成就可以了。
3-1、扫描端口
第一种:使用SerialPort类自带的GetPortNames的方法获取端口。
//使用API扫描
private string[] ScanPorts_API()
{
string[] portList = SerialPort.GetPortNames();
return portList;
}
第二种:获取注册表中的端口信息的方法获取端口。
//使用注册表信息扫描
private string[] ScanPorts_Regedit()
{
RegistryKey keyCom = Registry.LocalMachine.OpenSubKey("Hardware\\DeviceMap\\SerialComm");
string[] SubKeys = keyCom.GetValueNames();
string[] portList = new string[SubKeys.Length];
for (int i = 0; i < SubKeys.Length; i )
{
portList[i] = (string)keyCom.GetValue(SubKeys[i]);
}
return portList;
}
第三种:试错的方法获取有效的端口。
//试错方式扫描
private string[] ScanPorts_TryFail()
{
List<string> tempPost = new List<string>();
bool mark = false;
for (int i = 0; i < 10; i )
{
try
{
SerialPort sp = new SerialPort("COM" (i 1).ToString());
sp.Open();
sp.Close();
tempPost.Add("COM" (i 1).ToString());
mark = true;
}
catch (System.Exception)
{
continue;
}
}
if (mark)
{
string[] portList = tempPost.ToArray();
return portList;
}
else
{
return null;
}
}
推荐使用一、二中方法,第三种方法只能说是鬼点子,循环打开COM1-COM10端口:
- 能打开关闭,就存到List中。
- 报错就继续循环。
3-2、连接串口/断开串口
打开串口:
/// <summary>
/// 打开串口
/// </summary>
/// <param name="_portName">端口号</param>
/// <param name="_baudRate">波特率</param>
/// <param name="_parity">校验位</param>
/// <param name="dataBits">数据位</param>
/// <param name="_stopbits">停止位</param>
private void OpenSerialPort(string _portName, int _baudRate, Parity _parity, int dataBits, StopBits _stopbits)
{
try
{
if (!sp.IsOpen)
{
sp = new SerialPort(_portName, _baudRate, _parity, dataBits, _stopbits);//绑定端口
sp.Open();
}
}
catch (Exception e)
{
sp = new SerialPort();
Debug.Log(e);
}
}
对打开串口可能出现的问题进行了规避,比如说打开串口失败,就新建一个串口对象。
串口不在打开的状态,才去打开串口。
关闭串口:
/// <summary>
/// 关闭串口
/// </summary>
private void CloseSerialPort()
{
sp.Close();
}
很简单,就不多说了。
3-3、发送数据
发送数据封装了两个函数,一个是发送string类型数据,一个是发送byte[]数据:
/// <summary>
/// 发送数据
/// </summary>
/// <param name="_info">string数据</param>
private void SendData(string _info)
{
try
{
if (sp.IsOpen)
{
sp.WriteLine(_info);
}
else
{
sp.Open();
sp.WriteLine(_info);
}
}
catch (Exception ex)
{
Debug.Log(ex);
}
}
/// <summary>
/// 发送数据
/// </summary>
/// <param name="send">byte数据</param>
/// <param name="offSet">起始位</param>
/// <param name="count">byte长度</param>
private void SendData(byte[] send, int offSet, int count)
{
try
{
if (sp.IsOpen)
{
sp.Write(send, offSet, count);
}
else
{
sp.Open();
sp.Write(send, offSet, count);
}
}
catch (Exception ex)
{
Debug.Log(ex);
}
}
3-4、接收数据
接收数据有两种方式,一种是使用委托,一种是使用线程,下面分开讲解。
委托绑定回调函数,进行数据接收:
/// <summary>
/// 打开串口
/// </summary>
/// <param name="_portName">端口号</param>
/// <param name="_baudRate">波特率</param>
/// <param name="_parity">校验位</param>
/// <param name="dataBits">数据位</param>
/// <param name="_stopbits">停止位</param>
private void OpenSerialPort(string _portName, int _baudRate, Parity _parity, int dataBits, StopBits _stopbits)
{
try
{
if (!sp.IsOpen)
{
sp = new SerialPort(_portName, _baudRate, _parity, dataBits, _stopbits);//绑定端口
sp.Open();
//使用委托
sp.DataReceived = DataReceived;
}
}
catch (Exception ex)
{
sp = new SerialPort();
Debug.Log(ex);
}
}
/// <summary>
/// 接收数据 回调函数
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void DataReceived(object sender, SerialDataReceivedEventArgs e)
{
byte[] ReDatas = new byte[sp.BytesToRead];
sp.Read(ReDatas, 0, ReDatas.Length);//读取数据
DataProcessing(ReDatas);//数据处理
}
/// <summary>
/// 数据处理
/// </summary>
/// <param name="data">字节数组</param>
public void DataProcessing(byte[] data)
{
StringBuilder sb = new StringBuilder();
for (int i = 0; i < data.Length; i )
{
sb.AppendFormat("{0:x2}" "", data[i]);
}
Debug.Log(sb.ToString());
}
使用线程:
/// <summary>
/// 打开串口
/// </summary>
/// <param name="_portName">端口号</param>
/// <param name="_baudRate">波特率</param>
/// <param name="_parity">校验位</param>
/// <param name="dataBits">数据位</param>
/// <param name="_stopbits">停止位</param>
private void OpenSerialPort(string _portName, int _baudRate, Parity _parity, int dataBits, StopBits _stopbits)
{
try
{
if (!sp.IsOpen)
{
sp = new SerialPort(_portName, _baudRate, _parity, dataBits, _stopbits);//绑定端口
sp.Open();
//使用线程
Thread thread = new Thread(new ThreadStart(DataReceived));
thread.Start();
}
}
catch (Exception ex)
{
sp = new SerialPort();
Debug.Log(ex);
}
}
/// <summary>
/// 接收数据 线程
/// </summary>
private void DataReceived()
{
while (true)
{
if (sp.IsOpen)
{
int count = sp.BytesToRead;
if (count > 0)
{
byte[] readBuffer = new byte[count];
try
{
sp.Read(readBuffer, 0, count);
DataProcessing(readBuffer);//数据处理
}
catch (Exception ex)
{
Debug.Log(ex.Message);
}
}
}
Thread.Sleep(10);
}
}
推荐使用线程,因为Unity3D不支持SerialDataReceivedEventHandler,收数据的回调不会触发。
整体代码如下所示:
using Microsoft.Win32;
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO.Ports;
using System.Text;
using System.Threading;
using UnityEngine;
public class PortManager
{
private SerialPort sp;
#region 扫描端口
//使用API扫描
public string[] ScanPorts_API()
{
string[] portList = SerialPort.GetPortNames();
return portList;
}
//使用注册表信息扫描
public string[] ScanPorts_Regedit()
{
RegistryKey keyCom = Registry.LocalMachine.OpenSubKey("Hardware\\DeviceMap\\SerialComm");
string[] SubKeys = keyCom.GetValueNames();
string[] portList = new string[SubKeys.Length];
for (int i = 0; i < SubKeys.Length; i )
{
portList[i] = (string)keyCom.GetValue(SubKeys[i]);
}
return portList;
}
//试错方式扫描
public string[] ScanPorts_TryFail()
{
List<string> tempPost = new List<string>();
bool mark = false;
for (int i = 0; i < 10; i )
{
try
{
SerialPort sp = new SerialPort("COM" (i 1).ToString());
sp.Open();
sp.Close();
tempPost.Add("COM" (i 1).ToString());
mark = true;
}
catch (System.Exception)
{
continue;
}
}
if (mark)
{
string[] portList = tempPost.ToArray();
return portList;
}
else
{
return null;
}
}
#endregion
#region 打开串口/关闭串口
/// <summary>
/// 打开串口
/// </summary>
/// <param name="_portName">端口号</param>
/// <param name="_baudRate">波特率</param>
/// <param name="_parity">校验位</param>
/// <param name="dataBits">数据位</param>
/// <param name="_stopbits">停止位</param>
public void OpenSerialPort(string _portName, int _baudRate, Parity _parity, int dataBits, StopBits _stopbits)
{
try
{
sp = new SerialPort(_portName, _baudRate, _parity, dataBits, _stopbits);//绑定端口
sp.Open();
//使用委托
//sp.DataReceived = DataReceived;
//使用线程
Thread thread = new Thread(new ThreadStart(DataReceived));
thread.Start();
}
catch (Exception ex)
{
sp = new SerialPort();
Debug.Log(ex);
}
}
/// <summary>
/// 关闭串口
/// </summary>
public void CloseSerialPort()
{
sp.Close();
}
#endregion
#region 发送数据
/// <summary>
/// 发送数据
/// </summary>
/// <param name="_info">string数据</param>
public void SendData(string _info)
{
try
{
if (sp.IsOpen)
{
sp.WriteLine(_info);
}
else
{
sp.Open();
sp.WriteLine(_info);
}
}
catch (Exception ex)
{
Debug.Log(ex);
}
}
/// <summary>
/// 发送数据
/// </summary>
/// <param name="send">byte数据</param>
/// <param name="offSet">起始位</param>
/// <param name="count">byte长度</param>
public void SendData(byte[] send, int offSet, int count)
{
try
{
if (sp.IsOpen)
{
sp.Write(send, offSet, count);
}
else
{
sp.Open();
sp.Write(send, offSet, count);
}
}
catch (Exception ex)
{
Debug.Log(ex);
}
}
#endregion
#region 接收数据
/// <summary>
/// 接收数据 回调函数
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
public void DataReceived(object sender, SerialDataReceivedEventArgs e)
{
byte[] ReDatas = new byte[sp.BytesToRead];
sp.Read(ReDatas, 0, ReDatas.Length);//读取数据
DataProcessing(ReDatas);//数据处理
}
/// <summary>
/// 接收数据 线程
/// </summary>
public void DataReceived()
{
while (true)
{
if (sp.IsOpen)
{
int count = sp.BytesToRead;
if (count > 0)
{
byte[] readBuffer = new byte[count];
try
{
sp.Read(readBuffer, 0, count);
DataProcessing(readBuffer);//数据处理
}
catch (Exception ex)
{
Debug.Log(ex.Message);
}
}
}
Thread.Sleep(10);
}
}
/// <summary>
/// 数据处理
/// </summary>
/// <param name="data">字节数组</param>
public void DataProcessing(byte[] data)
{
StringBuilder sb = new StringBuilder();
for (int i = 0; i < data.Length; i )
{
sb.AppendFormat("{0:x2}" "", data[i]);
}
Debug.Log(sb.ToString());
}
#endregion
}
四、实际使用
因为,我没有串口硬件,就推荐一款比较好用的软件VSPD:
打开虚拟串口工具,新建两个串口:
在Unity中新建脚本,去扫描串口:
using System.IO.Ports;
using UnityEngine;
public class TestPort : MonoBehaviour
{
PortManager portManager;
void Start()
{
portManager = new PortManager();
string[] portArray = portManager.ScanPorts_TryFail();//使用试错函数,可以解决COM被占用问题
foreach (string port in portArray)
{
Debug.Log(port);
}
}
}
PortManager类就是上一节编写代码的名称,运行如下图所示:
然后,打开串口调试工具,打开串口:
修改代码:
using System.IO.Ports;
using UnityEngine;
public class TestPort : MonoBehaviour
{
PortManager portManager;
void Start()
{
portManager = new PortManager();
string[] portArray = portManager.ScanPorts_TryFail();//使用试错函数,可以解决COM被占用问题
portManager.OpenSerialPort(portArray[0], 9600, Parity.None, 8, StopBits.None);
portManager.SendData("12345");
}
}
运行程序,就可以让Unity3D与串口进行数据通信了:
这篇好文章是转载于:学新通技术网
- 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
- 本站站名: 学新通技术网
- 本文地址: /boutique/detail/tanhicigke
-
photoshop保存的图片太大微信发不了怎么办
PHP中文网 06-15 -
photoshop扩展功能面板显示灰色怎么办
PHP中文网 06-14 -
word里面弄一个表格后上面的标题会跑到下面怎么办
PHP中文网 06-20 -
《学习通》视频自动暂停处理方法
HelloWorld317 07-05 -
TikTok加速器哪个好免费的TK加速器推荐
TK小达人 10-01 -
Android 11 保存文件到外部存储,并分享文件
Luke 10-12 -
excel下划线不显示怎么办
PHP中文网 06-23 -
微信公众号没有声音提示怎么办
PHP中文网 03-31 -
微信运动停用后别人还能看到步数吗
PHP中文网 07-22 -
excel打印预览压线压字怎么办
PHP中文网 06-22