0

I have an incoming packet that reads 7E0302403F387E from a serial port.

start and end flag is 7E, FCS/CRC is 3F38 and the data is 030240. The FCS is calculated per the algorithm specified in RFC 1662. https://www.rfc-editor.org/rfc/rfc1662#appendix-A

This is the code I'm using to generate the FCS for 030240:

static byte[] HexToBytes(string input)
    {
        byte[] result = new byte[input.Length / 2];
        for (int i = 0; i < result.Length; i++)
        {
            result[i] = Convert.ToByte(input.Substring(2 * i, 2), 16);
        }
        return result;
    }


    public static class Crc16
    {
        const ushort polynomial = 0x8408;
        static readonly ushort[] table = new ushort[256];

        public static ushort ComputeChecksum(byte[] bytes)
        {
            ushort crc = 0;
            for (int i = 0; i < bytes.Length; ++i)
            {
                byte index = (byte)(crc ^ bytes[i]);
                crc = (ushort)((crc >> 8) ^ table[index]);
            }
            return crc;
        }

        static Crc16()
        {
            ushort value;
            ushort temp;
            for (ushort i = 0; i < table.Length; ++i)
            {
                value = 0;
                temp = i;
                for (byte j = 0; j < 8; ++j)
                {
                    if (((value ^ temp) & 0x0001) != 0)
                    {
                        value = (ushort)((value >> 1) ^ polynomial);
                    }
                    else
                    {
                        value >>= 1;
                    }
                    temp >>= 1;
                }
                table[i] = value;
            }
        }
    }

This is how I call it:

//string input = "00 03 00 02 04 00";
string input = "030240";
var bytes = HexToBytes(input);
string hexe = Crc16.ComputeChecksum(bytes).ToString("x2");

I should get an FCS of 3F38 but instead I'm getting 9ED0. What am I doing wrong?

Edit 1:

I'm reading ~\0\u0003\0\u0002\u0004\0?8~ from the serial port. I am able to convert that into either 7E-03-02-40-3F-38-7E or 7e-5c-30-5c-75-30-30-30-33-5c-30-5c-75-30-30-30-32-5c-75-30-30-30-34-5c-30-3f-38-7e. The first is too short and the second is too long. I should be getting 10 bytes. Any tips?

Edit 2:

I am using ASCII_To_Hex to convert ~\0\u0003\0\u0002\u0004\0?8~ to 7E-03-02-40-3F-38-7E

public string ASCII_To_Hex(string ASCII)
    {
        char[] charValues = dataIN.ToCharArray();
        string hexOutput = "";
        foreach (char _eachChar in charValues)
        {
            // Get the integral value of the character.
            int value = Convert.ToInt32(_eachChar);
            // Convert the decimal value to a hexadecimal value in string form.
            hexOutput += String.Format("{0:X}", value);
            // to make output as your eg 
            //  hexOutput +=" "+ String.Format("{0:X}", value);

        }
        return hexOutput;
    }

What changes can I make here to correctly decipher the incoming packet?

Edit 3:

I made the following changes based on Mark's suggestion:

public static class Crc16
    {
        const ushort polynomial = 0x1021;

        static readonly ushort[] table = new ushort[256];

        public static ushort ComputeChecksum(byte[] bytes)
        {
            ushort crc = 0xffff;
            for (int i = 0; i < bytes.Length; ++i)
            {
                byte index = (byte)(crc ^ bytes[i]);
                crc = (ushort)((crc >> 8) ^ table[index]);
            }
            return (ushort)~crc;
        }

        static Crc16()
        {
            ushort value;
            ushort temp;
            for (ushort i = 0; i < table.Length; ++i)
            {
                value = 0;
                temp = i;
                for (byte j = 0; j < 8; ++j)
                {
                    if (((value ^ temp) & 0x0001) != 0)
                    {
                        value = (ushort)((value >> 1) ^ polynomial);
                    }
                    else
                    {
                        value >>= 1;
                    }
                    temp >>= 1;
                }
                table[i] = value;
            }
        }
    }

This is how I'm calling it:

string hex = "00-03-00-02-04-00";
        hex = hex.Replace("-", "");
        var bytes = HexToBytes(hex);
        string hexe = Crc16.ComputeChecksum(bytes).ToString("X2");

I'm getting FO93 instead of 389B

Community
  • 1
  • 1
MuGa
  • 27
  • 5

1 Answers1

2

Based on the linked appendix, the initial value of the CRC is 0xffff, not 0, and you need to exclusive-or the result with 0xffff to get the FCS to put in the packet.

However that still doesn't work for your example packet. I can only guess that your example packet was not correctly extracted or identified.

Update:

From the OP comment below and some deduction, the actual data in the message is "~\x00\x03\x00\x02\x04\x00\x9b\x38~". The OP left out three zero bytes for some reason, and the OP printed it in a way that obscured one byte as a question mark.

Now the CRC calculates correctly as 0x389b, stored in the message little-endian as 0x9b 0x38. To get that, you must apply my comments above on the initial value and final exclusive-or.

Mark Adler
  • 101,978
  • 13
  • 118
  • 158
  • Im reading "~\0\u0003\0\u0002\u0004\0?8~" from the serial port. I am able to convert that into either 7E-03-02-40-3F-38-7E or 7e-5c-30-5c-75-30-30-30-33-5c-30-5c-75-30-30-30-32-5c-75-30-30-30-34-5c-30-3f-38-7e. The first is too short and the second is too long. I should be getting 10 bytes. Any tips? – MuGa Mar 14 '21 at 18:17
  • @MuGa You need to put that information in your question. – Mark Adler Mar 14 '21 at 18:46
  • Could you explain the deductions you made to get the actual data? – MuGa Mar 15 '21 at 12:24
  • 1
    Simply that the question mark, which is how unprintable bytes are always shown, is actually the low byte of the CRC and not a question mark, seeing as how the high byte of the CRC is correct since it happened to be printable. – Mark Adler Mar 15 '21 at 13:59
  • So the CRC is ?8 which should convert to 0x3F(?) and 0x38. How did you get 0x9B and 0x38? What about the u's? – MuGa Mar 15 '21 at 14:29
  • The question mark is often what you get when you try to output an unprintable character to your screen. It might have actually _been_ a question mark, but more likely if you have made the terrible mistake of trying to output binary data directly to your screen, it could have been most any byte with the high bit set. – Mark Adler Mar 15 '21 at 19:12
  • What you should be doing instead is taking your byte stream, being careful to _not_ interpret it is a string of characters (for which binary stream will have invalid unicode combinations, since it's not unicode), and print it as a series of hexadecimal values. In your case, `7e 00 03 00 02 04 00 9b 38 7e`. If you are not displaying your data that way for debugging purposes, then you are doing it wrong. – Mark Adler Mar 15 '21 at 19:15
  • The `u`'s that you are seeing is because you are doing it wrong, and treating it as a string of characters instead of a stream of bytes. As a string of characters, it is interpreting control characters (most values less than space) as unicode control characters, and displaying them that way as `\u00xx`. However when it gets to the byte `9b`, that is invalid unicode, so it's only option is to display that as a question mark. – Mark Adler Mar 15 '21 at 19:17
  • I am deducing that the question mark was printed for the value `9b` because I computed the CRC on the preceding bytes and got a high byte that is the next character, `38`, which happens to be printable as a string character, since that's an `8`. The low byte of that CRC is `9b`. – Mark Adler Mar 15 '21 at 19:18
  • **Stop displaying strings. Display bytes.** – Mark Adler Mar 15 '21 at 19:19
  • Thanks for all the help. I am now able to correctly identify the packet. I have a couple of more questions if you don't mind. 1) You said I need to exclusive-or the result with 0xffff to get the FCS to put in the packet. I changed value from 0 to 0xffff but I'm confused as to what you mean the result. Are you referring to this line: **`crc = (ushort)((crc >> 8) ^ table[index]);`** 2) When your calculating CRC, are you sending `00 03 00 02 04 00 9b 38` or `00 03 00 02 04 00`? – MuGa Mar 15 '21 at 23:42
  • The result is what you get at the end. – Mark Adler Mar 16 '21 at 02:00
  • The message is six bytes. – Mark Adler Mar 16 '21 at 02:00
  • If I understand correctly I should change: `crc = (ushort)((crc >> 8) ^ table[index]);` to this: `crc = ((ushort)((crc >> 8) ^ table[index])) ^ 0xffff;`. Once again, thanks for all the help. – MuGa Mar 16 '21 at 03:13
  • No, you do not understand correctly. The end is not in the middle of a loop! The end is the end. Change the `return crc;` to `return ~crc;`. – Mark Adler Mar 16 '21 at 03:26
  • I made the changes you suggested as seen in Edit 3. The problem is I'm getting 0xF093 instead of 0x389B – MuGa Mar 16 '21 at 14:10
  • Why did you change the polynomial to `0x1021`? I did not suggest that change. Change it back to `0x8408`. – Mark Adler Mar 16 '21 at 19:20
  • Thanks for all the help. One last question. You told me earlier to stop displaying strings and display bytes. The packet you helped me identify is a handshake packet that I have to respond to. I want send this payload: `0x00 0x01 0x00 0x03 0x04 0x01 0x00` and its CRC is `0xD7 0x2F`. All together the complete packet should look like this: ` 0xFE 0x00 0x01 0x00 0x03 0x04 0x01 0x00 0x2F 0xD7 0xFE`. I used SerialPort.Write Method to send those bytes with an offset of 0 and count of 11. I should get a response back but nothing. Can you see any errors in my logic? – MuGa Mar 16 '21 at 22:05
  • The CRC looks fine, but you indicated earlier that the sync bytes are `0x7e`, not `0xfe`. – Mark Adler Mar 16 '21 at 22:28
  • I've resolved it. Thanks for everything Mark. – MuGa Mar 16 '21 at 23:31
  • Hey mark, I ran into a problem and wanted to get your opinion on. I read this from the serial port: `7E 03 3E 02 2A 00 00 17 0D 00 00 33 E1 FF 04 02 02 00 00 22 7A 00 00 15 F0 00 00 3D 00 00 00 00 00 00 00 00 00 03 7A 00 00 00 E8 01 0A 56 1D 7E`. The CRC should be `56 1D` but the program you helped me with is giving me `75 52` (big endian). I made sure to perform byte un-stuffing on the message (changing `7D 5D` to just `7D` or `7D 5E` to `7E`). Do you have any idea what the problem is? – MuGa Apr 07 '21 at 00:48
  • I don't see a 7D in there. – Mark Adler Apr 07 '21 at 01:29
  • There isn't a 7D in the message. The byte un-stuffing doesn't apply to this message. I brought it up because its something I usually check for. – MuGa Apr 07 '21 at 02:02
  • I have no idea. Either the message is wrong (incorrectly identified or extracted, or simply in error), or it is using a different CRC or check algorithm. – Mark Adler Apr 07 '21 at 02:12