Search code examples
c#hexcrcmodbuscrc16

calculate CRC for Modbus RTU from strings


I have string: 02 03 04 50 00 01. I need to calculate the CRC for this string.

I have a function that counts a CRC:

public static UInt16 ModRTU_CRC(ushort[] buf, int len)
        {
            UInt16 crc = 0xFFFF;

            for (int pos = 0; pos < len; pos++)
            {
                crc ^= (UInt16)buf[pos];

                for (int i = 8; i != 0; i--)
                {
                    if ((crc & 0x0001) != 0)
                    {
                        crc >>= 1;
                        crc ^= 0xA001;
                    }
                    else
                        crc >>= 1;
                }
            }
            return crc;
        }

I want to cast a string to an array of ushort:

ushort[] result = cmd.Split(' ').Select(item => Convert.ToUInt16(item, 16)).ToArray();

but such an array is returned to me: 2 3 4 80 0 1.

Please tell me what should I do in order to correctly calculate the CRC.


Solution

  • It seems, you want to combine two bytes into ushort, i.e. for given

      string cmd = "02 03 04 50 00 01";
    

    you want to get

      {0x0203, 0x0405, 0x0001}
    

    If it's you case,

      using System.Linq;
    
      ...
    
      string cmd = "02 03 04 50 00 01";
    
      ushort[] result = cmd
        .Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries)
        .Select((value, index) => new { index, value = Convert.ToInt32(value, 16) })
        .GroupBy(item => item.index / 2, item => item.value)
        .Select(group => (UInt16)(group.Aggregate((s, a) => s * 256 + a)))
        .ToArray();
    

    Let's have a look:

      Console.WriteLine(string.Join(" ", data.Select(item => item.ToString("x4"))));
    

    Outcome:

      0203 0450 0001
    

    Edit: If you want not to combine first skip items (see comments below), you can try modifying GroupBy:

      int skip = 2;
    
      ushort[] data = source
        .Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries)
        .Select((value, index) => new { index, value = Convert.ToInt32(value, 16) })
        .GroupBy(item => item.index >= skip ? item.index / 2 : -item.index - 1, 
                 item => item.value)
        .Select(group => (UInt16)(group.Aggregate((s, a) => s * 256 + a)))
        .ToArray();
    
      Console.WriteLine(string.Join(" ", data.Select(item => item.ToString("x4"))));
    

    Outcome: (02 and 03 left intact, 04 combined with 50, 00 with 01)

      0002 0003 0450 0001