C#中modbus rtu和ascii互转

 modbus rtu和modbus ascii协议的数据部分是类似的,RTU模式下是数据部分+CRC校验码,ASCII模式下是“:”开始符+数据部分+LRC校验码+"\r \n"结束符,中间所需要做的转换就是ASCII模式下需要将数据部分转为ASCII码方式显示。

public byte[] ModbusAsciiToRtu(byte[] AsciiBytes)
        {
            if (AsciiBytes[0] != ':')
            {
                throw new InvalidOperationException("Invalid Modbus ASCII message format.");
            }

            // 去除起始符和结束符
            byte[] messageWithoutStartEnd = new byte[AsciiBytes.Length - 3];
            Array.Copy(AsciiBytes, 1, messageWithoutStartEnd, 0, AsciiBytes.Length - 3);
            if (messageWithoutStartEnd.Length % 2 != 0)
            {
                throw new InvalidOperationException("Invalid Modbus ASCII message length.");
            }
            // 检查是否为有效的十六进制字符
            bool IsValidHexChar(char c)
            {
                return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f');
            }
            // 将 ASCII 字符转换为字节值
            List<byte> messageByteList = new List<byte>();
            for (int i = 0; i < messageWithoutStartEnd.Length; i += 2)
            {
                char highNibble = (char)messageWithoutStartEnd[i];
                char lowNibble = (char)messageWithoutStartEnd[i + 1];

                if (!IsValidHexChar(highNibble) || !IsValidHexChar(lowNibble))
                {
                    continue;
                }

                string byteString = Encoding.ASCII.GetString(messageWithoutStartEnd, i, 2);
                messageByteList.Add(byte.Parse(byteString, NumberStyles.HexNumber));
            }
            byte[] messageBytes = messageByteList.ToArray();
            // 提取 LRC 校验码
            byte receivedLRC = messageBytes[messageBytes.Length - 1];
            // 去除 LRC 校验码
            byte[] messageWithoutLRC = new byte[messageBytes.Length - 1];
            Array.Copy(messageBytes, 0, messageWithoutLRC, 0, messageBytes.Length - 1);
            // 计算 LRC 校验码
            byte calculatedLRC = CalculateLRC(messageWithoutLRC);
            if (receivedLRC != calculatedLRC)
            {
                throw new InvalidOperationException("LRC mismatch in received Modbus ASCII message.");
            }
            ushort rtuMessageCRC = CalculateCRC16(messageWithoutLRC, 0, messageWithoutLRC.Length);
            byte[] rtuMessage = new byte[messageWithoutLRC.Length + 2];
            Array.Copy(messageWithoutLRC, 0, rtuMessage, 0, messageWithoutLRC.Length);
            rtuMessage[messageWithoutLRC.Length] = (byte)(rtuMessageCRC & 0xff);
            rtuMessage[messageWithoutLRC.Length + 1] = (byte)(rtuMessageCRC >> 8);
            return rtuMessage;
        }

这个为ASCII数据包转为RTU的数据包,下面还有RTU数据包转为ASCII数据包的方法,

public byte[] ModbusRtuToAscii(byte[] RtuBytes)
        {
            //提取CRC校验码并进行校验
            byte[] crc = new[] { RtuBytes[RtuBytes.Length - 2], RtuBytes[RtuBytes.Length - 1] };
            ushort crcValue = BitConverter.ToUInt16(crc, 0);
            //去除CRC校验码
            byte[] RtuBytesWithoutCRC = new byte[RtuBytes.Length-2];
            Array.Copy(RtuBytes, 0, RtuBytesWithoutCRC, 0, RtuBytes.Length-2);
            ushort rtuMessageCRC = CalculateCRC16(RtuBytes, 0, RtuBytes.Length-2);
            if (crcValue != rtuMessageCRC)
            {
                return null;
            }
            //数据转换为ASCII类型
            string asciiMessage = ConvertRTUFrameToASCII(RtuBytesWithoutCRC);
            asciiMessage = ":" + asciiMessage;
            //生成LRC校验码并拼接转换
            byte lrc = CalculateLRC(RtuBytesWithoutCRC);
            string asciiLRC = BitConverter.ToString(new byte[] { lrc });
            asciiMessage = asciiMessage+ asciiLRC + "\r\n";
            byte[] asciiBytes = Encoding.ASCII.GetBytes(asciiMessage);
            return asciiBytes;
        }

其中有几个辅助方法,用来进行CRC-16校验码的生成和LRC校验码的生成。crc生成后因为Modbus协议中CRC校验码是低位在前,所以要注意大小端的转换

public static ushort CalculateCRC16(byte[] data, int offset, int length)
        {
            ushort crc = 0xFFFF;

            for (int i = offset; i < offset + length; i++)
            {
                crc ^= (ushort)(data[i]);

                for (int j = 0; j < 8; j++)
                {
                    bool isLSBSet = (crc & 0x0001) != 0;
                    crc >>= 1;

                    if (isLSBSet)
                    {
                        crc ^= 0xA001;
                    }
                }
            }
            return crc;

        }
public string ConvertRTUFrameToASCII(byte[] rtuRequestFrame)
{
            StringBuilder asciiFrameBuilder = new StringBuilder();

            
            foreach (byte dataByte in rtuRequestFrame)
            {
                
                byte highNibble = (byte)((dataByte & 0xF0) >> 4);
                byte lowNibble = (byte)(dataByte & 0x0F);

                asciiFrameBuilder.Append((char)(highNibble + (highNibble < 10 ? '0' : 'A' - 10)));
                asciiFrameBuilder.Append((char)(lowNibble + (lowNibble < 10 ? '0' : 'A' - 10)));
            }

            return asciiFrameBuilder.ToString();
}
public static byte CalculateLRC(byte[] data)
{
            byte lrc = 0;

            for (int i = 0; i < data.Length; i++)
            {
                lrc += data[i];
            }

            lrc = (byte)((lrc ^ 0xFF) + 1);

            return lrc;
}

我属于C#和Modbus协议的初学者,如有不对或者需要改进的地方,欢迎大家讨论指导。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值