unity串口通信全流程

工具

  1. 虚拟串口调试工具 Virtual Serial Port Driver 6.9
    虚拟串口工具
  2. 串口调试工具 sscom5.13.1
    串口调试工具
  3. 如果串口的数据包是字符串类型的,可以直接使用串口调试工具去转换。如果是double类型的数据包,需要自己去转一下,这里贴一个简易的代码
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;

public class double_convert_byte : MonoBehaviour {
    double[] message = new double[3];
    List<byte> listData = new List<byte>();
    // Use this for initialization
    void Start () {
        message[0] =;
        message[1] = ;
        message[2] = ;
        for(int i=0;i<3;i++)
        {
            listData.AddRange(BitConverter.GetBytes(message[i]));
        }
        var bytes = listData.ToArray();
        var hex = BitConverter.ToString(bytes, 0).Replace("-", string.Empty).ToLower();

        Debug.Log(hex);

    }

    // Update is called once per frame
    void Update () {

    }
}

串口读取及其解包

1.串口读取代码

 using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.IO.Ports;
using System.Threading;
using System;
using System.Text;
using System.Runtime.InteropServices;

namespace data_structure
{
    public class PortControl : MonoBehaviour
    {
        [Header("串口名")] public string portName = "COM2";
        [Header("波特率")] public int baudRate = 115200;
        [Header("效验位")] public Parity parity = Parity.None;
        [Header("数据位")] public int dataBits = 100;
        [Header("停止位")] public StopBits stopBits = StopBits.One;

        private SerialPort sp = null;
        private Thread dataReceiveThread;

        private byte[] datasBytes;
        int i = 0;
        private byte OneByte;
        private byte[] OtherBytes;

        ArrayList m_CmdRcv = new ArrayList(); //存放接收到的数据

        //接收当前状态量数据
        public Object_State_Struct data_state = new Object_State_Struct();
        int cmdsize = 0;
        byte firstHeader = 0x4A; //数据包帧头
        byte secondHeader = 0x58;

        private void Start()
        {

            OpenPortControl();
        }
        /// <summary>
        /// 开启串口
        /// </summary>
        public void OpenPortControl()
        {
            sp = new SerialPort(portName, baudRate, parity, dataBits, stopBits);
            //串口初始化
            if (!sp.IsOpen)
            {
                sp.Open();
            }
            dataReceiveThread = new Thread(ReceiveData);//该线程用于接收串口数据 
            dataReceiveThread.Start();
        }
        /// <summary>
        /// 关闭串口
        /// </summary>
        public void ClosePortControl()
        {
            if (sp != null && sp.IsOpen)
            {
                sp.Close();//关闭串口
                sp.Dispose();//将串口从内存中释放掉
            }
        }

        private void ReceiveData()
        {
            int bytesToRead = 0;
            while (true)
            {
                if (sp != null && sp.IsOpen)
                {
                    try
                    {
                        datasBytes = new byte[1024];
                        bytesToRead = sp.Read(datasBytes, 0, datasBytes.Length);//将串口中的数据插入到datasBytes中的第0位
                        //Debug.Log(bytesToRead);
                        if (bytesToRead == 0)
                        {
                            continue;
                        }
                        else
                        {


                            i++;
                            if (i == 1)
                            {
                                OneByte = datasBytes[0];
                            }
                            else if (i == 2)
                            {
                                List<byte> listData = new List<byte>();
                                listData.Add(OneByte);
                                listData.AddRange(datasBytes);//将串口数据组合成完整的包
                                OtherBytes = listData.ToArray();
                                i = 0;
                                Decode_data(OtherBytes, OtherBytes.Length);//解包程序
                                Debug.Log(OtherBytes);
                            }
                            //Debug.Log(strbytes);
                        }

                    }
                    catch (Exception e)
                    {
                        Debug.Log(e.Message);
                    }
                }
                Thread.Sleep(100);
            }
        }

        /// <summary>
        /// 数据解包
        /// </summary>
        public void Decode_data(byte[] receiveData, int recvLength)
        {
            addRange(receiveData, recvLength); //把接收到的数据暂存为m_CmdRcv
            int fPz = m_CmdRcv.IndexOf((Byte)firstHeader); //查找消息头0x4A的位置;fPz=0,第一位;fPz>0,非第一位
            bool flagofFramTip = false; //数据帧头查找标志位,初始化为false
            cmdsize = Marshal.SizeOf(typeof(Object_State_Struct));

            while (!flagofFramTip)
            {
                if (fPz < 0) { m_CmdRcv.Clear(); flagofFramTip = true; }
                if (fPz > 0) { m_CmdRcv.RemoveRange(0, fPz); fPz = m_CmdRcv.IndexOf((Byte)firstHeader); }
                if (fPz == 0)
                {
                    if (m_CmdRcv.Count >= cmdsize)
                    {
                        if (secondHeader == (Byte)m_CmdRcv[fPz + 1])
                        {
                            //计算校验和
                            IntPtr pCmdRcv = Marshal.AllocHGlobal(cmdsize);
                            Byte check = 0;
                            ArrayList cmdData = new ArrayList();
                            for (int k = 0; k < cmdsize - 1; k++)
                            {
                                check += (Byte)m_CmdRcv[k];
                                cmdData.Add(m_CmdRcv[k]);
                            }
                            //判断校验和
                            if (check == (Byte)m_CmdRcv[cmdsize - 1])
                            {
                                // 认为此包正确
                                cmdData.Add(check);
                                //将cmdData的数据拷贝到pCmdRcv,方便后续的结构体直接赋值
                                Marshal.Copy((Byte[])cmdData.ToArray(typeof(Byte)), 0, pCmdRcv, cmdsize);
                                switch ((Byte)m_CmdRcv[fPz + 2])
                                {
                                    case 0x41:
                                        {
                                            data_state = (Object_State_Struct)Marshal.PtrToStructure(pCmdRcv, typeof(Object_State_Struct));
                                        }
                                        break;
                                    default:
                                        break;
                                }
                                //移除检验完的帧长
                                m_CmdRcv.RemoveRange(0, cmdsize);

                            }
                            //校验和不匹配
                            else
                            {
                                m_CmdRcv.RemoveRange(0, 2);
                            }
                        }
                        //第二位识别帧不匹配
                        else
                        {
                            m_CmdRcv.RemoveRange(0, 1);
                        }
                    }
                    //识别帧小于识别帧长
                    else
                    {
                        flagofFramTip = true;
                    }
                }
                //帧头小于识别帧长
                else { flagofFramTip = true; }
                // 重新寻找帧头
                fPz = m_CmdRcv.IndexOf((Byte)firstHeader);
            }
        }


        /// <summary>
        /// Byte数组转换为ArrayList
        /// </summary>
        /// <param name="receiveData">字符数组</param>
        /// <param name="len">数组长度</param>
        /// <returns></returns>
        void addRange(Byte[] receiveData, int len)
        {
            for (int i = 0; i < len; i++)
            {
                m_CmdRcv.Add(receiveData[i]);
            }
        }

        void OnApplicationQuit()
        {
            ClosePortControl();
        }
    }
}

结构体格式(应与协议一致)

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;

namespace data_structure
{
    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    public struct Object_State_Struct
    {
        /// <summary>
        /// 0.同步字节1
        /// </summary>
        public byte header1;
        /// <summary>
        /// 1.同步字节2
        /// </summary>
        public byte header2;
        /// <summary>
        /// 2.帧长(包含校验位)
        /// </summary>
        public byte len;
        /// <summary>
        /// 3.识别帧
        /// </summary>
        public byte ID;
        public double posX;
        /// 140.校验和
        /// 0~139字节累加和
        /// </summary>
        public byte checksum;
    }
}

 上一篇
深度学习细节 深度学习细节
Pytorchnn内部的关系 叶子张量leaf tensor: 反向传播时,只保留属性requires_grad和is_leaf为真的导数 requires_grad为真,is_leaf为假时,此张量的导数作为中间结果用于计算叶子节点的导
2021-02-06
下一篇 
利用unity制作自己的目标检测数据集 利用unity制作自己的目标检测数据集
搭建场景 首先搭建需要进行训练的目标场景 对需要进行标注的三维模型设置标签,这里是为了便于射线去检测目标物体进而生成mask。利用射线检测目标模型碰撞 这里直接放代码 Ray ray; RaycastHit hitInfo
2020-12-19
  目录