nrf24_packet_decoder.py 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. #
  2. # NRF24L01+ Enhanced ShockBurst packets decoder
  3. #
  4. payload_len_default = 4
  5. packets = (
  6. "10101010 11101110 00000011 00001000 00001011 01000111 000100 10 0 10101010 10101010 10101010 10101010 00011101",
  7. "10101010 11001000 11001000 11000011 110011 10 0 00001011 00000011 00000101 00000000 0010001100100000",
  8. "10101010 11001000 11001000 11000100 000100 11 1 00001011 00000011 00000101 00000000 0010010011100010",
  9. "10101010 11001000 11001000 11000100 00001011 00000011 00000101 00000010 1000010101000010",
  10. "10101010 11001000 11001000 11000000 110011 10 0 11110101 00000010 00000011 00000000 0000111001000000",
  11. "01010101 01000000 01101000 00010101 000000 00 0 0100100000100000",
  12. # '01010101 01000010 11100100 10100110 01010101 01000100 110011 00 0 10010101 10110011 01100100 10101100 10101011 01010010 01111100 01001010 1100110100110001',
  13. )
  14. def bin2hex(x):
  15. def bin2hex_helper(r):
  16. while r:
  17. yield r[0:2].upper()
  18. r = r[2:]
  19. if len(x) == 0:
  20. return
  21. fmt = "{0:0" + str(int(len(x) / 8 * 2)) + "X}"
  22. hex_data = fmt.format(int(x, 2))
  23. return list(bin2hex_helper(hex_data))
  24. def bin2hexlong(b):
  25. b = b.replace(" ", "")
  26. out = ""
  27. n = 8
  28. for i in range(0, len(b), n):
  29. b2 = b[i : i + n]
  30. out = out + "{0:02X}".format(int(b2.ljust(8, "0"), 2))
  31. return out
  32. def split_packet(packet, parts):
  33. """Split a string of 1s and 0s into multiple substrings as specified by parts.
  34. Example: "111000011000", (3, 4, 2) -> ["111", "0000", "11", "000"]
  35. :param packet: String of 1s and 0s
  36. :param parts: Tuple of length of substrings
  37. :return: list of substrings
  38. """
  39. out = []
  40. packet = packet.replace(" ", "")
  41. for part_length in parts:
  42. out.append(packet[0:part_length])
  43. packet = packet[part_length:]
  44. out.append(packet)
  45. return out
  46. def parse_packet(packet, address_length, ESB):
  47. """Split a packet into its fields and return them as tuple."""
  48. if ESB:
  49. preamble, address, payload_length, pid, no_ack, rest = split_packet(
  50. packet=packet, parts=(8, 8 * address_length, 6, 2, 1)
  51. )
  52. payload, crc = split_packet(
  53. packet=rest,
  54. parts=(
  55. (
  56. payload_len_default
  57. if int(payload_length, 2) > 32
  58. else int(payload_length, 2)
  59. )
  60. * 8,
  61. ),
  62. )
  63. else:
  64. preamble, address, rest = split_packet(
  65. packet=packet, parts=(8, 8 * address_length)
  66. )
  67. crc = packet.rsplit(" ", 1)[1]
  68. payload = rest[0 : len(rest) - len(crc)]
  69. payload_length = pid = no_ack = ""
  70. assert preamble in ("10101010", "01010101")
  71. assert len(crc) in (8, 16)
  72. return preamble, address, payload_length, pid, no_ack, payload, crc
  73. def crc(bits, size=8):
  74. """Calculate the crc value for the polynomial initialized with 0xFF/0xFFFF)
  75. :param size: 8 or 16 bit crc
  76. :param bits: String of 1s and 0s
  77. :return:
  78. :polynomial: 1 byte CRC - standard is 0x107 = 0b100000111 = x^8+x^2+x^1+1, result the same for 0x07
  79. :polynomial: 2 byte CRC - standard is 0x11021 = X^16+X^12+X^5+1, result the same for 0x1021
  80. """
  81. if size == 8:
  82. polynomial = 0x107
  83. crc = 0xFF
  84. else:
  85. polynomial = 0x11021
  86. crc = 0xFFFF
  87. max_crc_value = (1 << size) - 1 # e.g. 0xFF for mode 8bit-crc
  88. for bit in bits:
  89. bit = int(bit, 2)
  90. crc <<= 1
  91. if (crc >> size) ^ bit: # top most lfsr bit xor current data bit
  92. crc ^= polynomial
  93. crc &= max_crc_value # trim the crc to reject carry over bits
  94. # print('{:X}'.format(crc))
  95. return crc
  96. if __name__ == "__main__":
  97. for packet in packets:
  98. fld = packet.split(" ")
  99. address_length = -1
  100. ESB = True
  101. for f in fld:
  102. if len(f) == 6:
  103. break
  104. if len(f) == 0:
  105. ESB = False
  106. break
  107. address_length += 1
  108. (
  109. preamble,
  110. address,
  111. payload_length,
  112. pid,
  113. no_ack,
  114. payload,
  115. crc_received,
  116. ) = parse_packet(packet=packet, address_length=address_length, ESB=ESB)
  117. crc_size = len(crc_received)
  118. crc_received = "0x" + "{:X}".format(int(crc_received, 2))
  119. print(f"Packet: {packet}")
  120. print(
  121. "\n".join(
  122. (
  123. f"Hex: {bin2hexlong(packet)}",
  124. "Preamble: 0x%X" % int(preamble, 2),
  125. f"Address: {address_length} bytes - {bin2hex(address)}",
  126. )
  127. )
  128. )
  129. if ESB:
  130. print(
  131. "\n".join(
  132. (
  133. f"Payload length in packet: {int(payload_length, 2)}, used: {(payload_len_default if int(payload_length, 2) > 32 else int(payload_length, 2))}",
  134. f"Payload: {bin2hex(payload)}",
  135. f"Pid: {int(pid, 2)}",
  136. f"No_ack: {int(no_ack, 2) == 1}",
  137. )
  138. )
  139. )
  140. else:
  141. print(
  142. f"Not Enhanced ShockBurst packet, payload length: {int(len(payload) / 8)}"
  143. )
  144. print(f"Payload: {bin2hex(payload)}")
  145. print(f"CRC{crc_size}: {crc_received}")
  146. crc_calculated = "0x" + "{:X}".format(
  147. crc(address + payload_length + pid + no_ack + payload, size=crc_size)
  148. )
  149. if crc_received == crc_calculated:
  150. print("CRC is valid!")
  151. else:
  152. print(f"CRC mismatch! Calculated CRC is {crc_calculated}.")
  153. print("-------------")