I'm trying to write a function for my Arduino that take in a hex char *
and returns its hex checksum. I've been able to parse and iterate through my char *
but I haven't had the correct checksum return. Can someone help me find my bug or a better way to compute this?
So initially I tried following this tutorial for CRC32 and modifying it to CRC32C. Then I saw this code and decided that was easier for me to understand, this is what I based my current code on.
Here is my current code:
static unsigned long crc_table[256] = {
0x00000000, 0xf26b8303, 0xe13b70f7, 0x1350f3f4, 0xc79a971f, 0x35f1141c, 0x26a1e7e8, 0xd4ca64eb,
0x8ad958cf, 0x78b2dbcc, 0x6be22838, 0x9989ab3b, 0x4d43cfd0, 0xbf284cd3, 0xac78bf27, 0x5e133c24,
0x105ec76f, 0xe235446c, 0xf165b798, 0x030e349b, 0xd7c45070, 0x25afd373, 0x36ff2087, 0xc494a384,
0x9a879fa0, 0x68ec1ca3, 0x7bbcef57, 0x89d76c54, 0x5d1d08bf, 0xaf768bbc, 0xbc267848, 0x4e4dfb4b,
0x20bd8ede, 0xd2d60ddd, 0xc186fe29, 0x33ed7d2a, 0xe72719c1, 0x154c9ac2, 0x061c6936, 0xf477ea35,
0xaa64d611, 0x580f5512, 0x4b5fa6e6, 0xb93425e5, 0x6dfe410e, 0x9f95c20d, 0x8cc531f9, 0x7eaeb2fa,
0x30e349b1, 0xc288cab2, 0xd1d83946, 0x23b3ba45, 0xf779deae, 0x05125dad, 0x1642ae59, 0xe4292d5a,
0xba3a117e, 0x4851927d, 0x5b016189, 0xa96ae28a, 0x7da08661, 0x8fcb0562, 0x9c9bf696, 0x6ef07595,
0x417b1dbc, 0xb3109ebf, 0xa0406d4b, 0x522bee48, 0x86e18aa3, 0x748a09a0, 0x67dafa54, 0x95b17957,
0xcba24573, 0x39c9c670, 0x2a993584, 0xd8f2b687, 0x0c38d26c, 0xfe53516f, 0xed03a29b, 0x1f682198,
0x5125dad3, 0xa34e59d0, 0xb01eaa24, 0x42752927, 0x96bf4dcc, 0x64d4cecf, 0x77843d3b, 0x85efbe38,
0xdbfc821c, 0x2997011f, 0x3ac7f2eb, 0xc8ac71e8, 0x1c661503, 0xee0d9600, 0xfd5d65f4, 0x0f36e6f7,
0x61c69362, 0x93ad1061, 0x80fde395, 0x72966096, 0xa65c047d, 0x5437877e, 0x4767748a, 0xb50cf789,
0xeb1fcbad, 0x197448ae, 0x0a24bb5a, 0xf84f3859, 0x2c855cb2, 0xdeeedfb1, 0xcdbe2c45, 0x3fd5af46,
0x7198540d, 0x83f3d70e, 0x90a324fa, 0x62c8a7f9, 0xb602c312, 0x44694011, 0x5739b3e5, 0xa55230e6,
0xfb410cc2, 0x092a8fc1, 0x1a7a7c35, 0xe811ff36, 0x3cdb9bdd, 0xceb018de, 0xdde0eb2a, 0x2f8b6829,
0x82f63b78, 0x709db87b, 0x63cd4b8f, 0x91a6c88c, 0x456cac67, 0xb7072f64, 0xa457dc90, 0x563c5f93,
0x082f63b7, 0xfa44e0b4, 0xe9141340, 0x1b7f9043, 0xcfb5f4a8, 0x3dde77ab, 0x2e8e845f, 0xdce5075c,
0x92a8fc17, 0x60c37f14, 0x73938ce0, 0x81f80fe3, 0x55326b08, 0xa759e80b, 0xb4091bff, 0x466298fc,
0x1871a4d8, 0xea1a27db, 0xf94ad42f, 0x0b21572c, 0xdfeb33c7, 0x2d80b0c4, 0x3ed04330, 0xccbbc033,
0xa24bb5a6, 0x502036a5, 0x4370c551, 0xb11b4652, 0x65d122b9, 0x97baa1ba, 0x84ea524e, 0x7681d14d,
0x2892ed69, 0xdaf96e6a, 0xc9a99d9e, 0x3bc21e9d, 0xef087a76, 0x1d63f975, 0x0e330a81, 0xfc588982,
0xb21572c9, 0x407ef1ca, 0x532e023e, 0xa145813d, 0x758fe5d6, 0x87e466d5, 0x94b49521, 0x66df1622,
0x38cc2a06, 0xcaa7a905, 0xd9f75af1, 0x2b9cd9f2, 0xff56bd19, 0x0d3d3e1a, 0x1e6dcdee, 0xec064eed,
0xc38d26c4, 0x31e6a5c7, 0x22b65633, 0xd0ddd530, 0x0417b1db, 0xf67c32d8, 0xe52cc12c, 0x1747422f,
0x49547e0b, 0xbb3ffd08, 0xa86f0efc, 0x5a048dff, 0x8ecee914, 0x7ca56a17, 0x6ff599e3, 0x9d9e1ae0,
0xd3d3e1ab, 0x21b862a8, 0x32e8915c, 0xc083125f, 0x144976b4, 0xe622f5b7, 0xf5720643, 0x07198540,
0x590ab964, 0xab613a67, 0xb831c993, 0x4a5a4a90, 0x9e902e7b, 0x6cfbad78, 0x7fab5e8c, 0x8dc0dd8f,
0xe330a81a, 0x115b2b19, 0x020bd8ed, 0xf0605bee, 0x24aa3f05, 0xd6c1bc06, 0xc5914ff2, 0x37faccf1,
0x69e9f0d5, 0x9b8273d6, 0x88d28022, 0x7ab90321, 0xae7367ca, 0x5c18e4c9, 0x4f48173d, 0xbd23943e,
0xf36e6f75, 0x0105ec76, 0x12551f82, 0xe03e9c81, 0x34f4f86a, 0xc69f7b69, 0xd5cf889d, 0x27a40b9e,
0x79b737ba, 0x8bdcb4b9, 0x988c474d, 0x6ae7c44e, 0xbe2da0a5, 0x4c4623a6, 0x5f16d052, 0xad7d5351
};
unsigned long crc_string(char *s)
{
unsigned long crc = 0xffffffff;
while (*s){
crc = crc_table[(crc ^ ((uint8_t) hex2int(*s ))) & 0xff] ^ (crc >> 8);
}
crc = ~crc;
return crc;
}
int hex2int(char ch)
{
if (ch >= '0' && ch <= '9')
return ch - '0';
if (ch >= 'A' && ch <= 'F')
return ch - 'A' 10;
if (ch >= 'a' && ch <= 'f')
return ch - 'a' 10;
return -1;
}
An example of a packet I'm working with is:
B100880000000000C840C000160C0E0E2400CD19921979194B00004B00004A00004B00004B00004A00004A00004B00004B00004B00004A00004A00004B00004A00004A00004B00004A00004B00004A00004B00004B00004B00004A00004A00004A00004B00004A00004B00004A00004A00004B00004B00004A00004B00004B00004A00004B00004B0D66BC1B0D0A
Where the checksum is: 0D66BC1B
And the checksum from my function is: 348BA995
The data entering crc_string is:
00000000C840C000160C0E0E2400CD19921979194B00004B00004A00004B00004B00004A00004A00004B00004B00004B00004A00004A00004B00004A00004A00004B00004A00004B00004A00004B00004B00004B00004A00004A00004A00004B00004A00004B00004A00004A00004B00004B00004A00004B00004B00004A00004B00004B
Other information:
The data is initially received at a byte array that is changed to a String of hex for simplicity of reading. Please see the following function for details:
void array_to_string(byte array[], unsigned int len, char buffer[]) {
for (unsigned int i = 0; i < len; i ) { // make this len*2 ? to avoid buffer overflow
byte nib1 = (array[i] >> 4) & 0x0F;
byte nib2 = (array[i] >> 0) & 0x0F;
buffer[i * 2 0] = nib1 < 0xA ? '0' nib1 : 'A' nib1 - 0xA;
buffer[i * 2 1] = nib2 < 0xA ? '0' nib2 : 'A' nib2 - 0xA;
}
buffer[len * 2] = '\0';
}
The following is a larger portion of the file I am working with. An example of currentDataString is equibilent to "example of a packet" that was listed above.
void array_to_string(byte array[], unsigned int len, char buffer[]) {
for (unsigned int i = 0; i < len; i ) { // make this len*2 ? to avoid buffer overflow
byte nib1 = (array[i] >> 4) & 0x0F;
byte nib2 = (array[i] >> 0) & 0x0F;
buffer[i * 2 0] = nib1 < 0xA ? '0' nib1 : 'A' nib1 - 0xA;
buffer[i * 2 1] = nib2 < 0xA ? '0' nib2 : 'A' nib2 - 0xA;
}
buffer[len * 2] = '\0';
}
static void notifyCallback(BLERemoteCharacteristic *pBLERemoteCharacteristic, byte pData[], size_t length, bool isNotify) {
array_to_string(pData, length, currentData);
Serial.print(F("Clean data: "));
Serial.println(currentData);
String currentDataString(currentData);
else if(currentDataString.indexOf("B100")!= -1){
Serial.println("b100 Data");
if(checkCRC(currentDataString)){
Serial.println("Good Data");
}
}
}
boolean checkCRC(String currentDataString){
int len = currentDataString.length();
String newCRC = currentDataString.substring(8,len - 12 );
char buf[len- 8 - 12];
newCRC.toCharArray(buf, len-8-12);
unsigned long newFunction = crc_string(buf);
Serial.println(crc_string(buf), HEX);
Serial.print(F("newCRC: "));
Serial.println(newCRC);
String GivenCRC = currentDataString.substring(len - 4 - 8 ,len - 4 );
Serial.print(F("GivenCRC: "));
Serial.println(GivenCRC);
return newCRC == GivenCRC;
}
CodePudding user response:
If you are sending raw bytes, this should work just fine:
Edit: sorry, the original solution only focused on the contents of generating the crc. I added a len
parameter as well.
unsigned long crc_string(char *s, int len)
{
unsigned long crc = 0xffffffff;
int i = 0;
for (; i < len; i){
crc = crc_table[((crc << 8) ^ *s ) & 0xff] ^ (crc >> 8);
}
crc = ~crc;
return crc;
}
Another way to do this, would be to send a seed
parameter so you can crc on the fly better.
unsigned long crc_string(char *s, int len, unsigned long seed)
Then, you begin by setting crc
to seed
instead of 0xffffffff
.
What you have would work if you had something like this:
const char* s = "ab0c135a";
crc_string(s);
I'd be really surprised if you are actually getting hex strings.
CodePudding user response:
The best solution would be to calculate the CRC before converting the byte array to a hex string. Add a minimal reproducible example to the question to get a detailed solution.
If the hex string representation is only for debugging/printing, then there might be alternatives that don't need the conversion of the whole byte array.
Your CRC table is designed for 8-bit input values while your function hex2int
converts a single character which represents 4 bits only.
This means that you feed wrong data into your CRC calculation.
This can be fixed by converting two characters of the hex-string input to a byte that is fed into the CRC calculation.
int hex2nibble(char ch)
{
if (ch >= '0' && ch <= '9')
return ch - '0';
if (ch >= 'A' && ch <= 'F')
return ch - 'A' 10;
if (ch >= 'a' && ch <= 'f')
return ch - 'a' 10;
return -1;
}
int hex2byte(char *p)
{
int byte = hex2nibble(*p );
/* If the 1st character was invalid we want to keep the -1. */
if(byte >= 0)
{
/* Cast to unsigned because bit operations on signed data might be implementation defined. */
unsigned int val = (unsigned int)byte;
val <<= 4;
val |= (unsigned int)hex2nibble(*p);
byte = (int)val;
}
return byte;
}
unsigned long crc_string(char *s)
{
unsigned long crc = 0xffffffff;
while (*s){
/* note: missing error handling for return value -1 */
crc = crc_table[(crc ^ ((uint8_t) hex2byte(*s))) & 0xff] ^ (crc >> 8);
s = 2;
}
crc = ~crc;
return crc;
}
I'm not able to test the proposed code. I did not check if the two CRC implementations linked from the question are compatible.
The table in the linked Arduino example is for 4-bit input and crc_update
splits the 8-bit input into two 4-bit values. This table could be used if you want to feed 4-bit values into the CRC calculation. The Arduino solution feeds the lower nibble first into the CRC calculation. The untested proposal below will feed the higher nibble first, so this calculation might be wrong.
static PROGMEM prog_uint32_t crc_table[16] = {
0x00000000, 0x1db71064, 0x3b6e20c8, 0x26d930ac,
0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c,
0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c,
0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c
};
unsigned long crc_string(char *s)
{
unsigned long crc = 0xffffffff;
while (*s){
crc = pgm_read_dword_near(crc_table ((uint8_t) hex2int(*s ) & 0x0f))
^ (crc >> 4);
}
crc = ~crc;
return crc;
}