Last active
July 28, 2021 14:27
-
-
Save Semnodime/8c843623d2bfa46af1da6cee56df8df9 to your computer and use it in GitHub Desktop.
CRC Calculation for the Nordic nRF24L01
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
"""Small snippet that demonstrates how to calculate the crc for packets transmitted via the Nordic NRF24L01.""" | |
def bin2hex(x): | |
def bin2hex_helper(r): | |
while r: | |
yield r[0:2].upper() | |
r = r[2:] | |
hex_data = hex(int(x, 2))[2:] | |
return list(bin2hex_helper(hex_data)) | |
def split_packet(packet, parts): | |
"""Split a string of 1s and 0s into multiple substrings as specified by parts. | |
Example: "111000011000", (3, 4, 2) -> ["111", "0000", "11", "000"] | |
:param packet: String of 1s and 0s | |
:param parts: Tuple of length of substrings | |
:return: list of substrings | |
""" | |
out = [] | |
packet = packet.replace(' ', '') | |
for part_length in parts: | |
out.append(packet[0:part_length]) | |
packet = packet[part_length:] | |
out.append(packet) | |
return out | |
def parse_packet(packet, address_length): | |
"""Split a packet into its fields and return them as tuple.""" | |
preamble, address, payload_length, pid, no_ack, rest = split_packet(packet=packet, | |
parts=(8, 8 * address_length, 6, 2, 1)) | |
payload, crc = split_packet(packet=rest, parts=(int(payload_length, 2) * 8,)) | |
assert preamble in ('10101010', '01010101') | |
assert len(crc) in (8, 16) | |
return preamble, address, payload_length, pid, no_ack, payload, crc | |
def crc(bits, init=0xFF, polynomial=0x107, mode=8): | |
"""Calculate the crc value for the polynomial initialized with 0xFF) | |
:param mode: 8 or 16 bit crc | |
:param init: Initial value of the crc registers | |
:param polynomial: standard is 0x107 = 0b100000111 = x^8+x^2+x^1+1 Note: The code does the same for 0x07 | |
:param bits: String of 1s and 0s | |
:return: | |
""" | |
crc = init | |
max_crc_value = (1 << mode) - 1 # e.g. 0xFF for mode 8bit-crc | |
for bit in bits: | |
bit = int(bit, 2) | |
crc <<= 1 | |
if (crc >> mode) ^ bit: # top most lfsr bit xor current data bit | |
crc ^= polynomial | |
crc &= max_crc_value # trim the crc to reject carry over bits | |
return crc | |
if __name__ == '__main__': | |
"""Split test packets into their fields and validate the crc.""" | |
packets = \ | |
( | |
'10101010 11101110 00000011 00001000 00001011 01000111 000100 00 0 10101010 10101010 10101010 10101010 10010010', | |
'10101010 11101110 00000011 00001000 00001011 01000111 000100 01 0 10101010 10101010 10101010 10101010 01010110', | |
'10101010 11101110 00000011 00001000 00001011 01000111 000100 10 0 10101010 10101010 10101010 10101010 00011101', | |
'10101010 11101110 00000011 00001000 00001011 01000111 000100 11 0 10101010 10101010 10101010 10101010 11011001', | |
) | |
for packet in packets: | |
preamble, address, payload_length, pid, no_ack, payload, crc_received = \ | |
parse_packet(packet=packet, address_length=5) | |
crc_received = hex(int(crc_received, 2)) # convert into hex format | |
print(packet) | |
print('\n'.join(( | |
f'preamble: {preamble}', | |
f'address: {bin2hex(address)}', | |
f'payload length: {int(payload_length, 2)}', | |
f'pid: {int(pid, 2)}', | |
f'no_ack: {int(no_ack, 2) == 1}', | |
f'payload: {bin2hex(payload)}', | |
f'crc: {crc_received}'))) | |
crc_calculated = hex(crc(address + payload_length + pid + no_ack + payload, mode=8)) | |
if crc_received == crc_calculated: | |
print('CRC is valid!') | |
else: | |
print(f'CRC mismatch! Calculated CRC is f{crc_calculated}.') | |
print('---------') |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment