CRC calculation

The other day I was working on my BT-Gateway project, I wanted to set up ESP-NOW connection between HX711 scale running with ESP8266 board and the gateway on ESP32-S3. At this time I am using Arduino IDE to develop against ESP8266 and ESP-IDF framework for ESP32. The example code for ESP-NOW in ESP-IDF is using CRC to validate the message integrity.

crc_cal = esp_crc16_le(UINT16_MAX, (uint8_t const *)buf, data_len);

The function is defined in esp_crc.h file as a wrapper for esp_rom_crc16_le() in esp_rom_crc.c alongside with a number of other functions for 32 bit CRC in both Big and Little Endians.

I started looking for a CRC function for ESP8266. The RTCUserMemory example comes with calculateCRC32() function which I copied over and made myself a connection.

Bummer! CRC calculated on both sides did not match! So I went ahead to look in the algorithm, and those are the few things that I found:

CRC algorithm implementation vary by a few parameters:

  • Number of bits, i.e. length (obvious)
  • Polynomial used in the algo
  • Initial value
  • XOR for the output (usually invert or not)
  • a number of other parameters

Moreover, there is a common check for CRC – for input of “123456789” (byte array 0x31, 0x32 … 0x39), there is a well-known expected value for different implementations. And there is a neat document listing the different implementations – [link] [PDF]. Running such test on both ESP32 and ESP8266 I was able to find which one is implemented on ESP32 and match the parameters in the function on ESP8266.

    // CRC-32/CKSUM
    // width=32 poly=0x04c11db7 init=0x00000000 refin=false refout=false xorout=0xffffffff check=0x765e7680 residue=0xc704dd7b name=”CRC-32/CKSUM”
    crc_cal = esp_crc32_be(UINT32_MAX, (uint8_t const *)buf, data_len);

Another curious implementation detail that I found, is that CRC can either be computed on the spot (as ESP8266 is doing), or calculated using a more lightweight algorithm which makes use of a 256-element table array of pre-calculated values, this is how it is implemented in ESP32/ESP-IDF.

Here is the implementation of the CRC function for ESP8266 which is compatible with esp_crc32_be() on ESP32:

#include <stdint.h>
#include <stddef.h>

// CRC-32/CKSUM
// width=32 poly=0x04c11db7 init=0x00000000 refin=false refout=false xorout=0xffffffff check=0x765e7680 residue=0xc704dd7b name=”CRC-32/CKSUM”
#define CRC32_POLY 0x04C11DB7
uint32_t crc32_be(const uint8_t* data, size_t length) {
uint32_t crc = 0x0;

while (length–) {
crc ^= (*data++) << 24; // move byte into MSB of crc

for (uint8_t i = 0; i < 8; i++) {
if (crc & 0x80000000) {
crc = (crc << 1) ^ CRC32_POLY;
} else {
crc <<= 1;
} } }

return crc ^ 0xFFFFFFFF;
}

As you can see, I didn’t dive deep into the math, even though there is a comprehensive Wikipedia page for that [link] but only focused on whatever is necessary to solve compatibility issues.

 

Tag me on social media and let's discuss!