AngleReader.cs 7.75 KB
using System.Collections;
using System.Collections.Generic;
using System.IO.Ports;
using UnityEngine;
using System;

public class AngleReader : MonoBehaviour
{
    // 串口号,根据实际情况修改
    public string portName = "COM3";

    // 波特率,根据传感器设置修改
    public int baudRate = 9600;
    public int dataBits = 8;
    public Parity parity = Parity.None;
    public StopBits stopBits = StopBits.One;
    private SerialPort serialPort;

    public float angle;

    public string readAngleCommand = "01 03 00 01 00 02 95 CB";


    void Start()
    {
        // 创建串口对象并设置参数
        serialPort = new SerialPort(portName, baudRate, parity, dataBits, stopBits);
        try
        {
            // 打开串口
            serialPort.Open();
            Debug.Log("串口已成功打开");

            // 启动协程定时发送命令
            StartCoroutine(SendCommandPeriodically());
        }
        catch (System.Exception e)
        {
            Debug.LogError("无法打开串口: " + e.Message);
        }
    }

    void Update()
    {
        if (serialPort != null && serialPort.IsOpen)
        {
            try
            {
                if (serialPort.BytesToRead > dataBits)
                {
                    Debug.Log("BytesToRead: " + serialPort.BytesToRead);
                    byte[] buffer = new byte[serialPort.BytesToRead];
                    int bytesRead = serialPort.Read(buffer, 0, buffer.Length);

                    ParseModbusData(buffer, bytesRead);
                    serialPort.DiscardInBuffer();
                }
            }
            catch (System.Exception e)
            {
                Debug.LogError("读取串口数据时出错: " + e.Message);
                serialPort.DiscardInBuffer();
            }
        }
    }

    void OnDestroy()
    {
        if (serialPort != null && serialPort.IsOpen)
        {
            serialPort.Close();
            Debug.Log("串口已关闭");
        }
    }

    public float GetAngle()
    {
        return angle;
    }

    System.Collections.IEnumerator SendCommandPeriodically()
    {
        while (true)
        {
            if (serialPort != null && serialPort.IsOpen)
            {
                try
                {
                    // 去除十六进制命令中的空格
                    string cleanHexCommand = readAngleCommand.Replace(" ", "");
                    // 将十六进制字符串转换为字节数组
                    byte[] commandBytes = HexStringToByteArray(cleanHexCommand);
                    // byte[] commandBytes = { 0x01, 0x03, 0x00, 0x01, 0x00, 0x02, 0x95, 0xCB };
                    // 向串口发送字节数组
                    serialPort.Write(commandBytes, 0, commandBytes.Length);
                    // foreach (byte b in commandBytes) {
                    //     Debug.Log("已发送数据: " + b);
                    // }
                    Debug.Log("已发送命令: " + readAngleCommand);
                }
                catch (System.Exception e)
                {
                    Debug.LogError("发送数据时出错: " + e.Message);
                }
            }

            // 等待指定的时间间隔
            yield return new WaitForSecondsRealtime(0.1f);
        }
    }

    // 将十六进制字符串转换为字节数组的方法
    private byte[] HexStringToByteArray(string hex)
    {
        int length = hex.Length;
        byte[] bytes = new byte[length / 2];
        for (int i = 0; i < length; i += 2)
        {
            bytes[i / 2] = Convert.ToByte(hex.Substring(i, 2), 16);
        }

        return bytes;
    }

    // 将字节数组转换为十六进制字符串的方法
    private string BytesToHexString(byte[] bytes, int length)
    {
        System.Text.StringBuilder hex = new System.Text.StringBuilder(length * 2);
        for (int i = 0; i < length; i++)
        {
            hex.AppendFormat("{0:X2} ", bytes[i]);
        }

        return hex.ToString().Trim();
    }


    // 解析Modbus协议数据
    void ParseModbusData(byte[] data, int length)
    {
        if (length < 5 || !VerifyCRC(data, length))
        {
            Debug.LogError("数据帧长度不足或 CRC 校验失败");
            return;
        }

        byte slaveAddress = data[0];
        byte functionCode = data[1];

        switch (functionCode)
        {
            case 0x03: // 读取保持寄存器
                byte byteCount = data[2];
                if (length != 5 + byteCount)
                {
                    Debug.LogError("数据帧长度与字节计数不匹配");
                    return;
                }

                ushort[] registers = new ushort[byteCount / 2];
                for (int i = 0; i < byteCount / 2; i++)
                {
                    registers[i] = (ushort)((data[3 + i * 2] & 0xFF) << 8 | (data[4 + i * 2] & 0xFF));
                }

                Debug.Log($"从站地址: {slaveAddress}, 功能码: {functionCode}, 寄存器数量: {registers.Length}");

                // 本传感器共有2个寄存器,数据拼起来就是角度的16进制数据
                string hexString = "";
                for (int i = 0; i < registers.Length; i++)
                {
                    hexString += registers[i].ToString("X");
                    Debug.Log($"寄存器 {i}: {registers[i]}");
                }

                // 偶现某种情况下寄存器数据会丢失,此处确保两个寄存器数据都正常,否则本次数据忽略
                if (hexString.Length != 8)
                {
                    break;
                }

                float floatAngle = HexStringToFloatIEEE754(hexString);
                angle = floatAngle;
                Debug.Log($"计算角度为: {floatAngle}");

                break;
            default:
                Debug.LogError($"不支持的功能码: {functionCode}");
                break;
        }
    }

    private bool VerifyCRC(byte[] data, int length)
    {
        ushort receivedCRC = (ushort)((data[length - 2] & 0xFF) | ((data[length - 1] & 0xFF) << 8));
        ushort calculatedCRC = CalculateCRC16(data, length - 2);
        return receivedCRC == calculatedCRC;
    }

    private ushort CalculateCRC16(byte[] data, int length)
    {
        ushort crc = 0xFFFF;
        for (int i = 0; i < length; i++)
        {
            crc ^= (ushort)data[i];
            for (int j = 0; j < 8; j++)
            {
                if ((crc & 0x0001) == 0x0001)
                {
                    crc >>= 1;
                    crc ^= 0xA001;
                }
                else
                {
                    crc >>= 1;
                }
            }
        }

        return crc;
    }

    private float HexStringToFloatIEEE754(string hexString)
    {
        Debug.Log($"hexString: {hexString}");
        // 将十六进制字符串转换为 uint 类型
        if (uint.TryParse(hexString, System.Globalization.NumberStyles.HexNumber, null, out uint hexValue))
        {
            return HexToFloatIEEE754(hexValue);
        }
        else
        {
            // 处理转换失败的情况
            Console.WriteLine("十六进制字符串转换为 uint 失败,请检查输入。");
            return 0;
        }
    }

    private float HexToFloatIEEE754(uint hexValue)
    {
        Debug.Log($"hexValue: {hexValue}");
        // 分离符号位、指数位和尾数位
        int sign = (int)(hexValue >> 31) & 1;
        int exponent = ((int)(hexValue >> 23) & 0xFF) - 127;
        uint mantissa = hexValue & 0x7FFFFF;

        // 计算尾数的实际值
        double mantissaValue = 1.0 + (double)mantissa / Math.Pow(2, 23);

        // 计算浮点数的值
        double result = Math.Pow(-1, sign) * mantissaValue * Math.Pow(2, exponent);

        return (float)result;
    }
}