Communication between Siemens PLC and Unity3D丨Serial communication

foreword

The serial port communication script of U3D and plc or other software such as single-chip microcomputer needs to write a serial port service program on the host computer, which can realize the four basic contents of opening the serial port, closing the serial port, receiving, and sending. For friends who have data processing, you can Open another thread dedicated to data processing.

This article solves the problem that the Read() function in C# receives information once but reads it twice or multiple times under the condition that the accuracy of the data flow is slightly affected.

In order to facilitate direct transplantation, the following codes do not contain function names, just copy the code snippet directly to the function.

Update on April 28, 2023:
1. Previously, it was found that when the physical hardware circuit was disturbed, the link would be broken or the thread (coroutine) would be stuck. Later, it was found that the serial communication was most likely caused by external interference There are garbled characters, which cause the sequence numbers in the receiving thread to be garbled. This time, we mainly update the receiving module verification issue in the receiving thread.
2. At the same time, if the reading is incomplete, it is likely to be the speed of software reading >> the speed of receiving data by the serial port. The solution is to delay the start position of the receiving event function for a period of time, and the delay can be appropriately changed according to the length of the data. The time, my side is delayed by 300ms

Protocol

U3D is a serial port communication protocol, and PLC is a free port protocol.

code part

Preparation of Citation Documents

On the U3D side, you need to use

using System.IO.Ports;

The definitions of the five parameters of port number, baud rate, data bits, stop bits, and parity bits in this reference.

Data type declaration

    private string portName;   //端口号
    private int baudRate;         //波特率
    private Parity parity;		  //奇偶位
    private int dataBits;		  //数据位
    private StopBits stopBits;//停止位

After the five parameters are defined, start to write four basic function blocks, the most characteristic of which is the function of receiving data. I hope you can see if there is a better way. I will start here.

Write receive data function DataReceiveFunction()


	   int index = 0;       //用于记录此时的数据次序
        int ReadToBytes = 0;//记录长度
        byte[] readBuffer = new byte[20];
        int startIndex = -1;
        
	   while (true)
        {
            if (sp != null && sp.IsOpen)
            {
                try
                {
                	Thread.Sleep(300);
                    int n = sp.BytesToRead;

                    if (n > 0)//此处是发现接收缓存区出现内容则进行读取
                    {
                        readBuffer = new byte[sp.BytesToRead + 1];
                        ReadToBytes = sp.Read(readBuffer, 0, n);
                        //Debug.Log(n + "   "  + sp.ReadBufferSize);
                        
                        //if ((n == 10))  //用来判断是否符合接收长度,不符合则继续接收,因为Read()是分多次从缓冲区读取。
                                                //就可能出现一次读一位一次读12位的情况。
                        //{
                        //    index = 0;
                        //    for (int i = 0; i < n; i++)
                        //    {
                        //        if (index >= n) index = n - 1;
                        //        data[index] = readBuffer[i];                               //将数据存入data中
                        //        index++;
                        //    }
                        //}
                        //上面注释的部分为之前的弱校验,只是对接收缓存区内容大小做了一次判断
                        //下面为多次校验版本,通过查找“发送报文”的帧头“0xF4”来进行拷贝
                        //建议可以再这里多做几次校验。通过更改报文来确定收到的数据可靠
                        //CRC、长度、和等各种校验方式,这里仅对校验帧头帧尾举例。
                         for (int i = 0; i < readBuffer.Length; i++)
                        {
                            if (readBuffer[i] == 0xF4)
                            {
                                // 检查后面第10位是否为0xEF
                                if (i + 10 < readBuffer.Length && readBuffer[i + 10] == 0xEF)
                                {
                                    startIndex = i;
                                    break;
                                }
                            }
                        }
                        //当startIndex不为-1的时候表示找到,并将从此开始的整个报文拷贝到这里
                        if (startIndex != -1)
                        {
                            Array.Copy(readBuffer, startIndex, data, 0, 10);
                        }

                        //打印输入
                        string str = "";
                        for (int i = 0; i < index; i++)
                        {
                            str += Convert.ToString(data[i]) + " ";
                        }
                        Debug.Log(str + "   " + n + "   " + index + "   " + sp.ReadBufferSize);
                        str = "";
                        
						DataProcessingFunction(data);//数据处理函数,这个是根据自己需要怎么处理来写的,在这我就不献丑了。
                    }
                }
                catch (Exception ex)
                {
                    Debug.Log(ex);
                }
            }
            Thread.Sleep(100);
        }

Write and open the serial port function OpenPort()

Assign initial values ​​to five parameters

    SerialPort sp = null;//串口控制
    Thread dataReceiveThread;//定义一个线程
    
    //给五个参数赋上初值
	portName = COM1;
	baudRate = 9600;
	parity = Parity.None;
	dataBits = 8;
	stopBits = StopBits.None;
	
	//将参数传给sp
	sp = new SerialPort(portName, baudRate, parity, dataBits, stopBits);
	sp.ReadTimeout = 400;
	
	//使用try...catch...结构可以捕捉中途是否出现中断以及导致中断的原因。
	try
        {
            sp.Open();
        }
        catch (Exception ex)
        {
              Debug.Log(ex.Message);
        }
        
        //当串口打开后会报告一下打开串口信息,这个可以之后引入操作界面以供观察。
        if (sp.IsOpen)
        {
            Debug.Log("打开串口" + portName + " " + baudRate + " " + parity + " " + dataBits + " " + stopBits);

		//同时单独打开一个数据接收线程为
            dataReceiveThread = new Thread(new ThreadStart(DataReceiveFunction));//数据接收线程
            dataReceiveThread.Start();
        }

Close the serial port ClosePort()

//同打开串口一样也使用try...catch...
	   try
        {
            sp.Close();
        }
        catch (Exception ex)
        {
            Debug.Log(ex.Message);
        }

Send data WriteData(byte[] bys)

	   if (sp.IsOpen)
        {
            sp.Write(bys, 0, bys.Length);
        }

For the PLC side, refer to the free port protocol upward method. Any weird problems encountered in the future will continue to be updated.

Guess you like

Origin blog.csdn.net/qq_44879321/article/details/122244118