Unity3D软硬件Unity3D与串口通信 SerialPort类完全教程

Posted 恬静的小魔龙

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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求助,WPF嵌入unity3D并实现WPF与unity3D之间的通信

出售 unity3d串口插件

Unity3d使用json与javaserver通信

unity3d中串口的使用

Unity3D-消息机制实现与Android的通信

C#与unity3D通信 字节数组转为字符串