Problems with CRC16 calculation

I would like to implement the CRC16 checksum in my project. I used two available libraries of Github to calculate the CCITT checksum. The results from both libraries are the same. But I check on the website https://crccalc.com/, the result is different. So I definitely do something wrong that both libraries give me a wrong CRC.

Please help me fix my mistake.

#include "FastCRC.h"

FastCRC16 CRC16;

float Result_Rate = -999.9;
float Result_Acc_X = 2.0;
float Result_Acc_Y = 3.0;
float Result_Acc_Z = 4.0;

void printDataWithCRC(float *data1, float *data2, float *data3, float *data4) {
  byte *byteData1 = (byte *)(data1);
  byte *byteData2 = (byte *)(data2);
  byte *byteData3 = (byte *)(data3);
  byte *byteData4 = (byte *)(data4);
  byte buf[16] = {
    byteData1[0], byteData1[1], byteData1[2], byteData1[3],
    byteData2[0], byteData2[1], byteData2[2], byteData2[3],
    byteData3[0], byteData3[1], byteData3[2], byteData3[3],
    byteData4[0], byteData4[1], byteData4[2], byteData4[3]
  };
  for (int i = 0; i < sizeof(buf); i++)
  {
    Serial.print(buf[i], HEX); // Prints data: 9AF979C400040004040008040
  }
  Serial.println(CRC16.ccitt(buf, sizeof(buf)), HEX); //Prints CRC16: B944
}

void setup() {
  delay(100);
  Serial.begin(230400);
}

void loop() {
    printDataWithCRC(&Result_Rate, &Result_Acc_X, &Result_Acc_Y, &Result_Acc_Z);
    delay(1000);
}

What result did you get and what result do you expect based on the website ?

UKHeliBob:
What result did you get and what result do you expect based on the website ?

I got 0x23A9.

FastCRC uses the same conditions for calculation:
poly=0x1021 init=0xffff refin=false refout=false xorout=0x0000 check=0x29b1

Can you not just use the native C library facilities in crc16.h?
https://www.nongnu.org/avr-libc/user-manual/group__util__crc.html
All you need to do to use it in an Arduino sketch, is include the header. It's already installed.

So I definitely do something wrong that both libraries give me a wrong CRC.

Why do you believe the web site?

jremington:
Why do you believe the web site?

I have tried on two websites, both show the same result.

nebulae:
I have tried on two websites, both show the same result.

and you tried two libraries and they produced the same result as one another

Instead of printing (Serial.print()) the result, I have tried to write (Serial.write()) data and capture it using logic analyzer.

Serial.print() shows: 9AF979C400040004040008040490C
Logic analyzer shows: 9AF979C40000004000004040000080400C

Function to write binary data:

void printDataWithCRC(float *data1, float *data2, float *data3, float *data4) {
  byte *byteData1 = (byte *)(data1);
  byte *byteData2 = (byte *)(data2);
  byte *byteData3 = (byte *)(data3);
  byte *byteData4 = (byte *)(data4);
  byte buf[16] = {
    byteData1[0], byteData1[1], byteData1[2], byteData1[3],
    byteData2[0], byteData2[1], byteData2[2], byteData2[3],
    byteData3[0], byteData3[1], byteData3[2], byteData3[3],
    byteData4[0], byteData4[1], byteData4[2], byteData4[3]
  };

  Serial.write(buf, 16);
  Serial.write(CRC::crc16(buf, sizeof(buf)));
}

Additionally, I read the same binary output using Python, result it like this:

bytearray(b'\x9a\xf9y\xc4\x00\x00\x00@\x00\x00@@\x00\x00\x80@\x0c\x00')

If I unpack the data, I recieve the correct values:

data = bytearray(b'\x9a\xf9y\xc4\x00\x00\x00@\x00\x00@@\x00\x00\x80@\x0c\x00')
struct.unpack('f', data[0:4])
(-999.9000244140625,)

struct.unpack('f', data[4:8])
(2.0,)

struct.unpack('f', data[8:12])
(3.0,)

struct.unpack('f', data[12:16])
(4.0,)

CRC16 value will be:

data[16:18]
bytearray(b'\x0c\x00')

UKHeliBob:
and you tried two libraries and they produced the same result as one another

Yes, I have tried both. Actually, one of them is just a simple function:

uint16_t crc16(const uint8_t* buff, size_t size) {
  uint8_t* data = (uint8_t*)buff;
  uint16_t result = 0xFFFF;

  for (size_t i = 0; i < size; ++i)  {
    result ^= data[i];
    for (size_t j = 0; j < 8; ++j)    {
      if (result & 0x01) result = (result >> 1) ^ 0xA001;
      else result >>= 1;
    }
  }
  return result;
}

No thoughts at all about reply #3?

Serial.print(x, HEX) doesn't print leading zeros, so if you do

Serial.print(0x0A, HEX); 
Serial.print(0x0B, HEX);

It'll print "AB" instead of "0A0B".

Pieter

aarg:
No thoughts at all about reply #3?

  1. I'm curious why the problem occurs. Once I have tried to implement CRC16 for SPI, I was not successful. Now, I don't just want to finish my project, I would like to learn as well. :slight_smile:
  2. My aim is to use it also for non-AVR boards.
  3. I am confused and have many questions in my mind, e.g. why most of the CRC16 algorithms for C++ uses 0x1021 truncated polynomial (even all online CRC calculators), but Python crcmod library uses 0x11021 polynomial and has no option for truncated one.
  4. I want to use 0x11021 polynomial.
  5. FastCRC is faster:
CRC Benchmark
F_CPU: 8 MHz, length: 1024 Bytes.

Maxim (iButton) FastCRC:	Value:0x78, 		Time: 2112 us (3.88 mbs)
Maxim (iButton) builtin:	Value:0x78, 		Time: 8168 us (1.00 mbs)
MODBUS FastCRC:			Value:0x2487, 		Time: 3168 us (2.59 mbs)
MODBUS builtin: 		Value:0x2487, 		Time: 4336 us (1.89 mbs)
XMODEM FastCRC:			Value:0x190E, 		Time: 3072 us (2.67 mbs)
XMODEM builtin: 		Value:0x190E,	 	Time: 4840 us (1.69 mbs)
MCRF4XX FastCRC:		Value:0xF5F5,	 	Time: 3072 us (2.67 mbs)
MCRF4XX builtin:		Value:0xF5F5, 		Time: 3800 us (2.16 mbs)
KERMIT FastCRC:			Value:0x318, 		Time: 3080 us (2.66 mbs)
Ethernet FastCRC:		Value:0xE13699FF,	Time: 5192 us (1.58 mbs)

why most of the CRC16 algorithms for C++ uses 0x1021 truncated polynomial

Nonsense. There are many, many CRC16 algorithms and CRC16 polynomials. Even after you have chosen the polynomial, you get to choose from several other arbitrary factors, such as the arbitrary "initial XOR", arbitrary "final XOR" and whether the bits are processed from left to right or right to left, and whether the bytes are processed right to left or vice versa.

There is no good reason to choose one over the other for your own personal use, as they all have the same basic function.

You should do some reading to get a better understanding. This overview article identifies by name 12 different CRC16 algorithms, and there are many more.

Keep in mind that there are several different ways to implement any given CRC16 algorithm (lookup table, direct calculation, or mixture of both).

PieterP:
Serial.print(x, HEX) doesn't print leading zeros, so if you do

Serial.print(0x0A, HEX); 

Serial.print(0x0B, HEX);



It'll print "AB" instead of "0A0B".

Pieter

But not in real binary transmission, right? And more interesting than that, the data is only 17 bytes, last x00 is missing. What? CRC16 should be 2 bytes.

At the same time I see the payload from the logic analyzer is correct. I took the hex data from logic analyzer, divided them into four hex strings and converted into float number back. Everything is as it ought to be:

struct.unpack('f', bytes.fromhex('9AF979C4'))
(-999.9000244140625,)

struct.unpack('f', bytes.fromhex('00000040'))
(2.0,)

struct.unpack('f', bytes.fromhex('00004040'))
(3.0,)

struct.unpack('f', bytes.fromhex('00008040'))
(4.0,)

jremington:
Nonsense. There are many, many CRC16 algorithms and CRC16 polynomials. There are other factors, such as the arbitrary "initial XOR", arbitrary "final XOR" and whether the bits are processed from left to right or right to left. There is no good reason to choose one over the other, as they all have the same basic function.

You should do some reading to get a better understanding. [This overview article](http://why most of the CRC16 algorithms for C++ uses 0x1021 truncated polynomial) identifies by name 12 different CRC16 algorithms, and there are many more.

Yes. I totally agree with you. That's why I am curious why the all examples I see are mostly the same. (Would you please fix the link you shared?)

Guys, I'm taking your precious time. I wouldn't bother you too much. I'll check everything from A to Z and come back.

nebulae:
But not in real binary transmission, right? And more interesting than that, the data is only 17 bytes, last x00 is missing. What? CRC16 should be 2 bytes.

What is a “real binary transmission”?

This code is wrong:

for (int i = 0; i < sizeof(buf); i++)
  {
    Serial.print(buf[i], HEX); // Prints data: 9AF979C400040004040008040
  }

You have to insert leading zeros for each byte:

for (int i = 0; i < sizeof(buf); i++)
  {
    if (buf[i] < 0x10)
      Serial.print('0');
    Serial.print(buf[i], HEX);
  }

nebulae:

  1. I'm curious why the problem occurs. Once I have tried to implement CRC16 for SPI, I was not successful. Now, I don't just want to finish my project, I would like to learn as well. :slight_smile:
  2. My aim is to use it also for non-AVR boards.

I think it's worth knowing what is already implemented in the standard libraries, so you aren't constantly re-inventing the wheel. I'm pretty sure crc16.h would be available in most C compilers, not just AVR-GCC.

Also, how do you know it's slower than "fastCRC" or whatever? Have you benchmarked it?

Fixed the link, sorry.

The only time you need to use a particular CRC16 is when you have to match the results of one in use on another computer or particular piece of equipment. Otherwise, feel free to use one you like, for your own purposes.

PieterP:
What is a "real binary transmission"?

I mean, the actual hardware data transmission, not what software shows us.

aarg:
I think it's worth knowing what is already implemented in the standard libraries, so you aren't constantly re-inventing the wheel. I'm pretty sure crc16.h would be available in most C compilers, not just AVR-GCC.

Also, how do you know it's slower than "fastCRC" or whatever? Have you benchmarked it?

I just want to stick Arduino with Python. Yes, I have shared the benchmark results in my previous post.

not what software shows us.

I think you mean, what your incorrect use of Serial.print shows you.

nebulae:
Yes, I have shared the benchmark results in my previous post.

I don't see results for crc16.h in your list.