How to calculate this CRC using Python?
Solution 1
According to the cited document, the algorithm is actually a standard 16 Bit CCITT CRC. This can be calculated with crcmod
.
Here you go:
import crcmod
# this is a standard CCITT CRC even if it does not look like
# (crcmod applies xorOut to initCrc, so initCrc is in reality 0xffff, not 0)
_CRC_FUNC = crcmod.mkCrcFun(0x11021, initCrc=0, xorOut=0xffff)
data = bytearray.fromhex("023b010000000000")
crc = _CRC_FUNC(data)
data.append(crc & 0xff)
data.append(((crc >> 8) & 0xff))
print (data.hex())
Output: 023b010000000000ff2c
Solution 2
You need to apply that algorithm to each byte of your message. A slight complication is that the algorithm given in the Aurora PDF file assumes the calculation is being performed with 8 bit unsigned arithmetic. To handle that in Python we can use a bitmask of 0xff. Here's a slightly optimized version of that code.
def crc_16(msg):
lo = hi = 0xff
mask = 0xff
for new in msg:
new ^= lo
new ^= (new << 4) & mask
tmp = new >> 5
lo = hi
hi = new ^ tmp
lo ^= (new << 3) & mask
lo ^= new >> 4
lo ^= mask
hi ^= mask
return hi << 8 | lo
# Test
msg = bytes.fromhex("023b010000000000")
out = crc_16(msg)
hi, lo = out >> 8, out & 0xff
print('{:04x} = {:02x} {:02x}'.format(out, hi, lo))
output
2cff = 2c ff
The above code works, but there are simpler ways to calculate CRCs. And we can use a table to speed up the process, if you need to calculate a lot of CRCs.
As the Wikipedia Cyclic redundancy check article mentions, CRC algorithms are usually specified in terms of a polynomial encoded as a hexadecimal number. Here's a function that does that using the reversed polynomial representation.
def crc_16_CCITT(msg):
poly = 0x8408
crc = 0xffff
for byte in msg:
for _ in range(8):
if (byte ^ crc) & 1:
crc = (crc >> 1) ^ poly
else:
crc >>= 1
byte >>= 1
return crc ^ 0xffff
To speed things up, we can compute a table.
def make_crc_table():
poly = 0x8408
table = []
for byte in range(256):
crc = 0
for bit in range(8):
if (byte ^ crc) & 1:
crc = (crc >> 1) ^ poly
else:
crc >>= 1
byte >>= 1
table.append(crc)
return table
table = make_crc_table()
def crc_16_fast(msg):
crc = 0xffff
for byte in msg:
crc = table[(byte ^ crc) & 0xff] ^ (crc >> 8)
return crc ^ 0xffff
# Test
msg = bytes.fromhex("023b010000000000")
out = crc_16_fast(msg)
hi, lo = out >> 8, out & 0xff
print('{:04x} = {:02x} {:02x}'.format(out, hi, lo))
If you like, you can print the table & paste it into your script, so that you don't have to compute the table every time you run the script.
Related videos on Youtube
Simone Luconi
Updated on June 11, 2022Comments
-
Simone Luconi almost 2 years
I need to calculate this CRC using Python for the communication with Aurora (ABB) solar inverter.
This is the document: http://www.drhack.it/images/PDF/AuroraCommunicationProtocol_4_2.pdf in the last page there are the instructions to calculate the CRC, i need to do that in python.
The message that i have is
MESSAGE_GRID_VOLTAGE = bytes.fromhex("023b010000000000")
The results should be:
CRC_L = FF
CRC_H = 2C
Then i need to send the message complete with the CRC like this:
MESSAGE_GRID_VOLTAGE = bytes.fromhex("023b010000000000ff2c")
How can i do that in python? Thanks!
Here is the code that i tried:
message = "023b010000000000" BccLo= int ("FF",16) BccHi= int("FF", 16) New = int(message, 16) New = New ^ BccLo Tmp=New << 4 New=Tmp ^ New Tmp=New >> 5 BccLo=BccHi BccHi= New ^ Tmp Tmp=New << 3 BccLo=BccLo ^ Tmp Tmp=New >> 4 BccLo=BccLo ^ Tmp CRC_L = ~BccLo CRC_H = ~BccHi
-
Klaus D. over 5 yearsWhat have you tried so far?
-
Simone Luconi over 5 yearsI tried using the ^ operator, but it doesen't support bytes, maybe i should try to convert the bytes to a number and then recovert it to bytes, i have the fear to mess up something with theese to many conversion.
-
Simone Luconi over 5 yearsi found this code in c#, still i have the same problem if i try to convert it into python: dreamincode.net/forums/topic/193903-crc-calculation-problem
-
Simone Luconi over 5 yearsi tried it in c#, but it doesn't work, the crc is not correct, even if i try with the example messages written by the user.
-
Klaus D. over 5 yearsShow us some Python code! Here on SO it is expected that you show us the efforts you made to solve the problem.
-
Simone Luconi over 5 yearsi added it to the question
-
Simone Luconi over 5 yearsmade some progress, i updated the answer, but the output is not correct
-
Mark Adler about 2 yearsFor future reference, specification of that particular CRC:
width=16 poly=0x1021 init=0xffff refin=true refout=true xorout=0xffff check=0x906e residue=0xf0b8 name="CRC-16/IBM-SDLC"
-
-
Simone Luconi over 5 yearsThanks, I appreciate the work that you have done to explain the process, but the answer from the user "Adrian W" its simple to implement
-
PM 2Ring over 5 years
crcmod
isn't a standard Python module, but I suppose it's easy enough to install from PyPI. -
Adrian W over 5 yearsYou're right, crcmod is not part of the standard distribution. You can install it with
pip install crcmod
. See also pypi.org/project/crcmod -
skyking about 2 yearsI'd suggest updating the answer to reflect that crcmod isn't standard.
-
Admin about 2 yearsAs it’s currently written, your answer is unclear. Please edit to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers in the help center.