Giter Site home page Giter Site logo

crc's Introduction

Arduino CI Arduino-lint JSON check GitHub issues

License: MIT GitHub release PlatformIO Registry

CRC

Arduino library with CRC8, CRC12, CRC16, CRC32 and CRC64 functions.

Description

Goal of this library is to have a flexible and portable set of generic CRC functions and classes.

The CRCx classes have a number of added values. Most important is that they allow one to verify intermediate CRC values. This is useful if one sends a "train of packets" which include a CRC so far. This detects both errors in one single packet but also optional missing packets, or even injected packets.

Another trick one can do with the class CRCx is to change the polynome or the reverse flag runtime during the process. This makes it harder to imitate.

Furthermore the class allows to add values in single steps and continue too.

Finally the class version gives more readable code (IMHO) as the parameters are explicitly set.

Note the classes have similar names as the static functions The class name is always (full) UPPER case. So CRC8 is a class and calcCRC8() is the function.

Related

Deeper tech info

and many other websites.

Interface CRC classes

#include "CRC8.h"

Base

The interfaces are very similar for CRC8, CRC12, CRC16, CRC32 and CRC64 class. The difference is the data type for polynome, start- and end-mask, and the returned CRC. The start mask is named initial and end-mask is named XOR-out.

Base

Use #include "CRC8.h"

The interfaces for CRC12, CRC16, CRC32 and CRC64 are similar to CRC8.

  • CRC8(polynome, initial, xorOut, reverseIn, reverseOut) Constructor to set all parameters at once.
  • void reset() set all internals to defaults of the constructor.
  • void restart() reset internal CRC and count only; reuses values for the other flags e.g polynome, XOR masks and reverse flags.
  • uint8_t calc() returns CRC calculated so far. This allows to check the CRC of a really large stream at intermediate moments, e.g. to link multiple packets.
  • crc_size_t count() returns number of values added since (re)start. The default == 0.
  • void add(value) add a single value to the CRC calculation.
  • void add(array, length) add an array of length values to the CRC calculation. In case of a warning/error for the array type, use casting e.g. (uint8_t *).
  • void add(array, length, yieldPeriod) as CRC calculations of large blocks can take serious time (in milliseconds), the classes call yield() after every yieldPeriod calls to keep RTOS environments happy. The call allows to add values with yield() to get optimal performance. The risk is missing context switching to handle interrupts etc. So use at own risk.

Parameters

These functions allows to set individual parameters of the CRC at runtime. The parameters do not have defaults so the user must set them explicitly.

  • void setPolynome(polynome) set polynome. Note: reset() and restart() sets a default polynome.
  • void setInitial(initial) set the start-mask.
  • void setXorOut(xorOut) set the end-mask.
  • void setReverseIn(reverseIn) reverse the bit pattern of input data (MSB vs LSB).
  • void setReverseOut(reverseOut) reverse the bit pattern of CRC (MSB vs LSB).
  • uint8_t getPolynome() return parameter set above or default.
  • uint8_t getInitial() return parameter set above or default.
  • uint8_t getXorOut() return parameter set above or default.
  • bool getReverseIn() return parameter set above or default.
  • bool getReverseOut() return parameter set above or default.

Example snippet

A minimal usage needs:

  • the constructor, the add() function and the calc() function.
#include "CRC32.h"
...

CRC32 crc;
  ...
  while (Serial.available())
  {
    int c = Serial.read();
    crc.add(c);
  }
  Serial.println(crc.calc());

Interface static functions

Use #include "CRC.h"

Most functions have a default polynome, same start and end masks, and default there is no reversing. However these parameters allow one to tweak the CRC in all aspects known. In all the examples encountered the reverse flags were set both to false or both to true. For flexibility both parameters are kept available.

  • uint8_t calcCRC8(array, length) idem with default polynome.
  • uint16_t calcCRC12(array, length) idem with default polynome.
  • uint16_t calcCRC16(array, length) idem with default polynome.
  • uint32_t calcCRC32(array, length) idem with default polynome.
  • uint64_t calcCRC64(array, length) - experimental version, no reference found except on Wikipedia.

The static functions calcCRC..() in this library also support yield.

The static CRC functions use fast reverse functions that can be also be used outside CRC context. Their usage is straightforward.

  • uint8_t reverse8bits(uint8_t in) idem.
  • uint16_t reverse16bits(uint16_t in) idem.
  • uint16_t reverse12bits(uint16_t in) idem.
  • uint32_t reverse32bits(uint32_t in) idem.
  • uint64_t reverse64bits(uint64_t in) idem.

reverse12bits is based upon reverse16bits, with a final shift. Other reverses can be created in similar way.

CrcParameters.h

Since version 1.0.0 the file CrcParameters.h is added to hold symbolic names for certain parameters (polynomes, etc..). These can be used in your code too to minimize the number of "magic HEX codes". If standard polynomes are missing, please open an issue and report, with reference.

(CrcParameters.h replaces and extends CRC_polynomes.h)

Future

Must

  • extended performance measurements 1.x.x version
    • both class and functions.

Should

  • add examples.
    • example showing multiple packages of data linked by their CRC. sort of "blockchain"

Could

  • table versions for performance? (performance - memory discussion).
  • stream version - 4 classes class?
  • setCRC(value) to be able to pick up where one left ?
    • can be done with setInitial()
    • needs getRawCRC() without reverse and end mask

Exotic CRC's ?

  • CRC1() // parity :)
  • CRC4() nibbles?
    • default polynome 0x03 ITU
  • One CRC() with #bits as parameter?
    • up to 64 bit for all missing ones?
    • performance penalty
  • One CRC() template class?

Won't

  • add a dump(Stream = Serial) to see all the settings at once. user can access parameters, so no need.

Support

If you appreciate my libraries, you can support the development and maintenance. Improve the quality of the libraries by providing issues and Pull Requests, or donate through PayPal or GitHub sponsors.

Thank you,

crc's People

Contributors

manchoz avatar robtillaart avatar vovagorodok avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

crc's Issues

Use little endian by default

I have CRC32 calculated using (android and python) libraries and it doesn't match with result. Only after:

crc.setStartXOR(0xFFFFFFFF);
crc.setEndXOR(0xFFFFFFFF);
crc.setReverseIn(true);
crc.setReverseOut(true);

it works.
Most of architectures (esp32/avr/arm/x86) has little endian. And for them we should use this combination.
What about using little endian by default, and allow to change in specific cases, when big endian is needed ?

CRC16.h or crc16.h?

Hi Rob.
I'm re-using an Arduino IDE sketch that I last successfully used probably 7 years ago, and it's now no longer compiling.
I'm getting the error Compilation error: util/crc16.h: No such file or directory
I've searched, found & added your library, but your library is spelled in capitol letters CRC16.h, whereas in my sketch it's crc16.h and of course therefore does not resolve things.

Has something changed in it's naming protocol, or may I have possibly used a different library.
Any help or guidance would be appreciated.

different crc32 result from crccalc.com/

Running included crc32 example sketch on an arduino uno i get:

/home/brd/Arduino/libraries/CRC/examples/CRC32_test/CRC32_test.ino
89A1897F
89A1897F
89A1897F
CDBAC17A
18

while https://crccalc.com/ with "123456789" input: ascii, output: hex, i get: 0xCBF43926

here the crccalc result link

What i'm missing?

Binary CRC8

Will this lib work on a 24bit string? I need to calculate the CRC8 for a 24bit binary string. I will append the CRC to the 24bit string to sent a SPI transaction from a teensy arduino to an ad74412r ADC device.

Can you point me to an example?

Thanks,
Chuck

Add constructors with all parameters at once

now you have to do

  CRC16 crc;
  crc.setPolynome(0x1021);
  crc.setStartXOR(0xFFFF);
  crc.setEndXOR(0xFFFF);
  crc.setReverseIn(true);
  crc.setReverseOut(true);

it would be easier if one had a constructor like

  CRC16 crc(0x1021, 0xFFFF, 0xFFFF, true, true);

input content (uint8_t *array) as HEX

Hi,

Have a possible to use the function crc16 when the value to calculate is in HEX ?

I am using this CRC calculation online to check the calc, when the content type is a String, the result is ok:

using the crc function:
image

result by func => 5E5A

online:
image

but we need the result in HEX:
image

result => D550

So, we not found in documentation if is possible to do that ๐Ÿ‘€

Thanks !

Yield condition is not updated in loop

Hello, I've just started using your library and after looking through the code a little I noticed the yield checks seem to be slightly off (either never called at all or called every itteration)

void CRC_X::add(const uint8_t * array, uint8_t length)
{
  _count += length;
  while (length--)
  {
    // reduce yield() calls
    if ((_count & 0xFF) == 0xFF) yield();
    _update(*array++);
  }
}

Should probably be:

void CRC_X::add(const uint8_t * array, uint8_t length)
{
  while (length--)
  {
   _count++;
    // reduce yield() calls
    if ((_count & 0xFF) == 0xFF) yield();
    _update(*array++);
  }
}

I have a slightly different solution in my fork (moved the check to add(value)) but here's my patch if you're interested: dlsloan@9338e98

Diverging CRC16 results

HI, I'm experimenting with your library using CRC16 to compare if json files on an ESP32 and on an iPhone are the same. I have an iOS app that sends json files to an ESP32 which needs to stay in sync. I'm getting divergent CRC results between your lib and the lib I use in the iOS app.

The polynomial are the same (0x1021) in both libs and I'm setting the BigEndianIn (true or false) option to match the same option in the iOS lib. Any ideas what I could be doing wrong, or a tip on how I can troubleshoot this?

ESP32 code:

uint16_t calculateCRC(fs::FS &fs, const String& filename, const String& extension) {
    if (fs.exists("/" + filename + extension)) {
      File file = fs.open("/" + filename + extension);
      crc.reset();
      crc.setPolynome(0x1021);
      crc.setReverseIn(true); //Also tried (false) 
      while (file.available()) {
        uint8_t c = file.read();
        crc.add(c);  
      } 
     return crc.getCRC();
  }
}

Swift code:
Usage:

func calculateCRC(data: Data) -> UInt16 {
        let byteArray: [UInt8] = Array(data)
        let crc16 = CRC16()
        crc16.append(byteArray)
        return crc16.currentValue
    }

Class:

/// Class to conveniently calculate CRC-16. It uses the CRC16-CCITT polynomial (0x1021)  by default
public class CRC16: CRC {

    /// The table that stores the precomputed remainders for all possible divisions for 1 byte
    /// This table is used as a lookup (to speed up the calculation)
    public let lookupTable: [UInt16]

    private(set) public var currentValue: UInt16 = 0

    /// Creates the instance for calculating CRC
    /// - Parameter polynomial: The polynomial to use. It uses CRC16-CCITT's polynomial (0x1021) by default
    public init(polynomial: UInt16 = 0x1021) {
        /// Generates the lookup table (make sure to generate it only once)
        self.lookupTable = (0...255).map { Self.crc16(for: UInt8($0), polynomial: polynomial) }
    }
    public func append(_ bytes: [UInt8]) {
        currentValue = crc16(for: bytes, initialValue: currentValue)
    }
    public func reset() {
        currentValue = 0
    }
}


extension CRC16 {
    /// Caculates CRC-16 of an array of Bytes (UInt8)
    private func crc16(for inputs: [UInt8], initialValue: UInt16 = 0) -> UInt16 {
        inputs.reduce(initialValue) { remainder, byte in
            let bigEndianInput = UInt16(byte).bigEndian
            let index = (bigEndianInput ^ remainder) >> 8
            return lookupTable[Int(index)] ^ (remainder << 8)
        }
    }
}

CRC8 SAE J1850 From CAN Data

Hi Robert,

I realize this isn't a bug or issue, but wondered if you would be interested to charge me to help me on this project? I am using a Arduino device to talk to the dash of a motorcycle over CAN. I need to transmit 8 bytes of data on message ID 543, which i have no issue doing using Arduino now, bytes 1-6 are based on ecu parameters like gear position, engine rpm, etc., byte 7 is a counter that goes from 128-191 (also no issue here), and the last byte is a CRC8 SAE J1850 with 0x1D polynomial, initial value 0xFF, final Xor value 0xFF.

Using one of the online tools I can get the correct answer, in the screenshot that I attached I get the correct 0x1D answer, but I am not sure how to use the example sketches that you have provided. I have tested using the same string "123456789" with the online tool, and I obtain the same answer as your example sketch. Where I am lost is that I typically get all of the CAN incoming data to the Arduino from another device into a variable called buf[0], buf[1]...buf[7]. All of the example sketches that I have seen so far all have all of the data contained directly in 1 place like your example

const char str[99] = "123456789";

A small snippet of the CAN code to receive CAN information into buf variable is below.

unsigned char len = 0;
if (CAN_MSGAVAIL == CAN.checkReceive()) 
 {         // check if data coming
        CAN.readMsgBuf(&len, buf);    // read data,  len: data length, buf: data buf
        unsigned long canId = CAN.getCanId();

Thanks for your consideration, I would be interested to know if you can help and how much I can donate in paypal support. Hopefully this makes sense, I can explain further if needed.

0x543 CAN Message

(updated code snippet)

The CRC16-Modbus calculation with the bytes swapped for Dwin displays

Hi, I'm trying the Dwin displays, which have CRC16, but they use the Modbus calculation with the bytes swapped. How can I get the calculation done by Arduino to be changed in the library instead of in the sketch?

https://github.com/RobTillaart/CRC/blob/master/examples/CRC16_test_modbus/CRC16_test_modbus.ino

For example, the code sent to the display should be this: 5A A5 06 83 20 00 01 E9 AA.

5A A5: header
06: length
83: Read VP command
20 00: VP addr
01: data
E9 AA: CRC16 checksum (for 83-20-00-01 only)

  • This CRC by Dwin is calculated from the command byte (byte 3) to the last byte of data (which means that byte 2 indicates how many bytes of data the message has, up to 255 bytes - 5 bytes of control: 250 bytes of data; being also included the 2 bytes of CRC16)

But the online calculator and this CRC16_test_modbus.ino returns the CRC16 Modbus (hex):

AA E9

It is 'AA E9' Instead of 'E9 AA' as Dwin sends for display in the 'SP Order' tab of the DGUS software.

how to get crc12?

I have a problem to program crc12 on arduino, can you provide a solution or explanation how to get crc12 with your library?

thanks very much

Hexa number as input

I Rob!

It's a cool CRC library. Useful for a n00b like me!

For Hexa inputs like 0xFFFF443443 or 0xAA78DD56898EE55EE33, how can I define the input?

Example:

uint8_t strX = 0x01010000; //does not work

 uint8_t  strX [4] = {0x01, 0x01, 0x00, 0x00};  //  it works, but I really have to break all into bytes?

Working code:

//
//    FILE: CRC16_test.ino
//  AUTHOR: Rob Tillaart
// PURPOSE: demo
//    DATE: 2021-01-20
//    (c) : MIT


#include "CRC16.h"
#include "CRC.h"



uint8_t  strX[4] = {0x01, 0x01, 0x00, 0x00};  

CRC16 crc;


void setup()
{
  SerialUSB.begin(115200);
  delay(5000);
  SerialUSB.println(__FILE__);

  // SerialUSB.println("Verified with - http://zorc.breitbandkatze.de/crc.html \n");
  

  test();
}


void loop()
{
  test();
  delay(5000);
}


void test()
{
  uint16_t  strCRC = 0;
  SerialUSB.print("cred ca CRC calculat: ");

  strCRC = crc16((uint8_t *) strX, 4, 0x8005, 0, 0, false, false);
  SerialUSB.println(strCRC, HEX);

}


// -- END OF FILE --

const Parameters?

crc* functions don't modify the array passed, shouldn't they be marked as const? This would allow these function to handle string-type input a lot better.

uint16_t crc16(const uint8_t* array, ....

Test: crc16("Test123", 16);

warning: 'deprecated' attribute directive ignored [-Wattributes]

Hello,
I get a lot of these warnings when using version1.0.x
I upgraded Arduino to 2.11 and they are still shown when compiling. I need to have warnings shown to keep my own code in decent shape :)

d:\Mina Dokument\Arduino\libraries\CRC\src/CRC.h:114:48: warning: 'deprecated' attribute directive ignored [-Wattributes] crc_size_t yieldPeriod = CRC_YIELD_DISABLED);

make it possible to enable / disable yield()

@dlsloan
from #17

Yield is a nice touch for long running functions, but it's nice to have an option with no yield in-case someone want's to run a crc calc from within a yield call (when not using an rtos that does context switching). In my fork I made it optional in the class, but I'll probably be changing that to something compile time in the future.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.