I have a terminal that communicates through RS232 COM with the computer. The protocol that I was given says that I have to send a certain combination of bytes and the CRC 16 IBM calculation of the data sent at the end
I was also given a C written application that I can test with, that application writes a log with send data and received data. In that log I see if I send the terminal this string hexString = "02 00 04 a0 00 01 01 03"
. I must also send this CRC16 IBM result of data 06 35.
I have managed to somehow translate the C method that was given as an example, into C#. But my result is far away from what I know I must receive.
I have tested sending the data from the log and everything is fine. I must have my calculation done wrong. Am I doing anything wrong here?
Here is my code:
CRC class:
public enum Crc16Mode : ushort
{
ARINC_NORMAL = 0XA02B, ARINC_REVERSED = 0xD405, ARINC_REVERSED_RECIPROCAL = 0XD015,
CCITT_NORMAL = 0X1021, CCITT_REVERSED = 0X8408, CCITT_REVERSED_RECIPROCAL = 0X8810,
CDMA2000_NORMAL = 0XC867, CDMA2000_REVERSED = 0XE613, CDMA2000_REVERSED_RECIPROCAL = 0XE433,
DECT_NORMAL = 0X0589, DECT_REVERSED = 0X91A0, DECT_REVERSED_RECIPROCAL = 0X82C4,
T10_DIF_NORMAL = 0X8BB7, T10_DIF_REVERSED = 0XEDD1, T10_DIF_REVERSED_RECIPROCAL = 0XC5DB,
DNP_NORMAL = 0X3D65, DNP_REVERSED = 0XA6BC, DNP_REVERSED_RECIPROCAL = 0X9EB2,
IBM_NORMAL = 0X8005, IBM_REVERSED = 0XA001, IBM_REVERSED_RECIPROCAL = 0XC002,
OPENSAFETY_A_NORMAL = 0X5935, OPENSAFETY_A_REVERSED = 0XAC9A, OPENSAFETY_A_REVERSED_RECIPROCAL = 0XAC9A,
OPENSAFETY_B_NORMAL = 0X755B, OPENSAFETY_B_REVERSED = 0XDDAE, OPENSAFETY_B_REVERSED_RECIPROCAL = 0XBAAD,
PROFIBUS_NORMAL = 0X1DCF, PROFIBUS_REVERSED = 0XF3B8, PROFIBUS_REVERSED_RECIPROCAL = 0X8EE7
}
public class Crc16
{
readonly ushort[] table = new ushort[256];
public ushort ComputeChecksum(params 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;
}
public byte[] ComputeChecksumBytes(params byte[] bytes)
{
ushort crc = ComputeChecksum(bytes);
return BitConverter.GetBytes(crc);
}
public Crc16(Crc16Mode mode)
{
ushort polynomial = (ushort)mode;
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;
}
}
}
Method to process the bytes received:
public ushort CalculateCRC(byte[] data)
{
Crc16 crcCalc = new Crc16(Crc16Mode.IBM_NORMAL);
ushort crc = crcCalc.ComputeChecksum(data);
return crc;
}
In this method you can select polynomial from the Enum.
Main in Program Class:
static void Main(string[] args) { try { Metode m = new Metode();
string hexString = "02 00 04 a0 00 01 01 03";
byte[] bytes = m.HexStringToByteArray(hexString);
ushort crc = m.CalculateCRC(bytes);
string hexResult;
int myInt = crc;
hexResult = myInt.ToString("X");
//Console.WriteLine(crc.ToString());
Console.WriteLine(hexResult);
Console.ReadLine();
}
catch (Exception ex)
{
Metode m = new Metode();
m.writeError(ex.Message);
}
}
Convert from hexstring to byte array:
public byte[] HexStringToByteArray(string hexString)
{
hexString = hexString.Replace(" ", "");
return Enumerable.Range(0, hexString.Length)
.Where(x => x % 2 == 0)
.Select(x => Convert.ToByte(hexString.Substring(x, 2), 16))
.ToArray();
}
Convert from byte array to hex string:
public string ByteArrayToHexString(byte[] byteArray)
{
return BitConverter.ToString(byteArray);
}
What am I doing wrong here?
UPDATE:
Thanks to @MarkAdler I have managed to translate the calculation. What I didn't notice until late was the fact that the CRC calculation should have been for online the DATA sent to the terminal, NOT the entire message!
So the hexString should have been in fact "a0 00 01 01", the data without the STX/length/ETX.
Here is the code for this particular CRC16 Calculus in C#:
public ushort CalculateCRC(byte[] data, int len)
{
int crc = 0, i = 0;
while (len-- != 0)
{
crc ^= data[i++] << 8;
for (int k = 0; k < 8; k++)
crc = ((crc & 0x8000) != 0) ? (crc << 1) ^ 0x8005 : (crc << 1);
}
return (ushort)(crc & 0xffff);
}