#!/usr/bin/python # -*- coding: utf-8 -*- # # This file lib_nrf24.py is a slightly tweaked version of Barraca's "pynrf24". # So this is my tweak for Raspberry Pi and "Virtual GPIO" ... # ... of Barraca's port to BeagleBone python ... (Joao Paulo Barraca ) # ... of maniacbug's NRF24L01 C++ library for Arduino. # Brian Lavery Oct 2014 # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # import sys import time if __name__ == '__main__': print (sys.argv[0], 'is an importable module:') print ("... from", sys.argv[0], "import lib_nrf24") print ("") exit() def _BV(x): return 1 << x class NRF24: MAX_CHANNEL = 127 MAX_PAYLOAD_SIZE = 32 # PA Levels PA_MIN = 0 PA_LOW = 1 PA_HIGH = 2 PA_MAX = 3 PA_ERROR = 4 # Bit rates BR_1MBPS = 0 BR_2MBPS = 1 BR_250KBPS = 2 # CRC CRC_DISABLED = 0 CRC_8 = 1 CRC_16 = 2 CRC_ENABLED = 3 # Registers CONFIG = 0x00 EN_AA = 0x01 EN_RXADDR = 0x02 SETUP_AW = 0x03 SETUP_RETR = 0x04 RF_CH = 0x05 RF_SETUP = 0x06 STATUS = 0x07 OBSERVE_TX = 0x08 CD = 0x09 RX_ADDR_P0 = 0x0A RX_ADDR_P1 = 0x0B RX_ADDR_P2 = 0x0C RX_ADDR_P3 = 0x0D RX_ADDR_P4 = 0x0E RX_ADDR_P5 = 0x0F TX_ADDR = 0x10 RX_PW_P0 = 0x11 RX_PW_P1 = 0x12 RX_PW_P2 = 0x13 RX_PW_P3 = 0x14 RX_PW_P4 = 0x15 RX_PW_P5 = 0x16 FIFO_STATUS = 0x17 DYNPD = 0x1C FEATURE = 0x1D # Bit Mnemonics */ MASK_RX_DR = 6 MASK_TX_DS = 5 MASK_MAX_RT = 4 EN_CRC = 3 CRCO = 2 PWR_UP = 1 PRIM_RX = 0 ENAA_P5 = 5 ENAA_P4 = 4 ENAA_P3 = 3 ENAA_P2 = 2 ENAA_P1 = 1 ENAA_P0 = 0 ERX_P5 = 5 ERX_P4 = 4 ERX_P3 = 3 ERX_P2 = 2 ERX_P1 = 1 ERX_P0 = 0 AW = 0 ARD = 4 ARC = 0 PLL_LOCK = 4 RF_DR = 3 RF_PWR = 6 RX_DR = 6 TX_DS = 5 MAX_RT = 4 RX_P_NO = 1 TX_FULL = 0 PLOS_CNT = 4 ARC_CNT = 0 TX_REUSE = 6 FIFO_FULL = 5 TX_EMPTY = 4 RX_FULL = 1 RX_EMPTY = 0 DPL_P5 = 5 DPL_P4 = 4 DPL_P3 = 3 DPL_P2 = 2 DPL_P1 = 1 DPL_P0 = 0 EN_DPL = 2 EN_ACK_PAY = 1 EN_DYN_ACK = 0 # Instruction Mnemonics R_REGISTER = 0x00 W_REGISTER = 0x20 REGISTER_MASK = 0x1F ACTIVATE = 0x50 R_RX_PL_WID = 0x60 R_RX_PAYLOAD = 0x61 W_TX_PAYLOAD = 0xA0 W_ACK_PAYLOAD = 0xA8 FLUSH_TX = 0xE1 FLUSH_RX = 0xE2 REUSE_TX_PL = 0xE3 NOP = 0xFF # Non-P omissions LNA_HCURR = 0x00 # P model memory Map RPD = 0x09 # P model bit Mnemonics RF_DR_LOW = 5 RF_DR_HIGH = 3 RF_PWR_LOW = 1 RF_PWR_HIGH = 2 # Signal Mnemonics LOW = 0 HIGH = 1 datarate_e_str_P = ["1MBPS", "2MBPS", "250KBPS"] model_e_str_P = ["nRF24L01", "nRF24l01+"] crclength_e_str_P = ["Disabled", "8 bits", "16 bits"] pa_dbm_e_str_P = ["PA_MIN", "PA_LOW", "PA_MED", "PA_HIGH"] child_pipe = [RX_ADDR_P0, RX_ADDR_P1, RX_ADDR_P2, RX_ADDR_P3, RX_ADDR_P4, RX_ADDR_P5] child_payload_size = [RX_PW_P0, RX_PW_P1, RX_PW_P2, RX_PW_P3, RX_PW_P4, RX_PW_P5] child_pipe_enable = [ERX_P0, ERX_P1, ERX_P2, ERX_P3, ERX_P4, ERX_P5] GPIO = None spidev = None def __init__(self, gpio, spidev): # It should be possible to instantiate multiple objects, with different GPIO / spidev # EG on Raspberry, one could be RPI GPIO & spidev module, other could be virtual-GPIO # On rpi, only bus 0 is supported here, not bus 1 of the model B plus self.GPIO = gpio # the GPIO module self.spidev = spidev # the spidev object/instance self.channel = 76 self.data_rate = NRF24.BR_1MBPS self.wide_band = False # 2Mbs data rate in use? self.p_variant = False # False for RF24L01 and true for RF24L01P (nrf24l01+) self.payload_size = 5 #*< Fixed size of payloads self.ack_payload_available = False #*< Whether there is an ack payload waiting self.dynamic_payloads_enabled = False #*< Whether dynamic payloads are enabled. self.ack_payload_length = 5 #*< Dynamic size of pending ack payload. self.pipe0_reading_address = None #*< Last address set on pipe 0 for reading. def ce(self, level): if self.ce_pin == 0: return # rf24-CE is optional. Tie to HIGH if not used. (Altho, left floating seems to read HIGH anyway??? - risky!) # Some RF24 modes may NEED control over CE. # non-powerdown, fixed PTX or RTX role, dynamic payload size & ack-payload: does NOT need CE. if level == NRF24.HIGH: self.GPIO.output(self.ce_pin, self.GPIO.HIGH) else: self.GPIO.output(self.ce_pin, self.GPIO.LOW) return def read_register(self, reg, blen=1): buf = [NRF24.R_REGISTER | ( NRF24.REGISTER_MASK & reg )] for col in range(blen): buf.append(NRF24.NOP) resp = self.spidev.xfer2(buf) if blen == 1: return resp[1] return resp[1:blen + 1] def write_register(self, reg, value, length=-1): buf = [NRF24.W_REGISTER | ( NRF24.REGISTER_MASK & reg )] ###if isinstance(value, (int, long)): # ng for python3. but value should never be long anyway if isinstance(value, int): if length < 0: length = 1 length = min(4, length) for i in range(length): buf.insert(1, int(value & 0xff)) value >>= 8 elif isinstance(value, list): if length < 0: length = len(value) for i in range(min(len(value), length)): buf.append(int(value[len(value) - i - 1] & 0xff)) else: raise Exception("Value must be int or list") return self.spidev.xfer2(buf)[0] def write_payload(self, buf): data_len = min(self.payload_size, len(buf)) blank_len = 0 if not self.dynamic_payloads_enabled: blank_len = self.payload_size - data_len txbuffer = [NRF24.W_TX_PAYLOAD] for n in buf: t = type(n) if t is str: txbuffer.append(ord(n)) elif t is int: txbuffer.append(n) else: raise Exception("Only ints and chars are supported: Found " + str(t)) if blank_len != 0: blank = [0x00 for i in range(blank_len)] txbuffer.extend(blank) return self.spidev.xfer2(txbuffer) def read_payload(self, buf, buf_len=-1): if buf_len < 0: buf_len = self.payload_size data_len = min(self.payload_size, buf_len) blank_len = 0 if not self.dynamic_payloads_enabled: blank_len = self.payload_size - data_len txbuffer = [NRF24.NOP for i in range(0, blank_len + data_len + 1)] txbuffer[0] = NRF24.R_RX_PAYLOAD payload = self.spidev.xfer2(txbuffer) del buf[:] buf.extend(payload[1:data_len + 1]) return data_len def flush_rx(self): return self.spidev.xfer2([NRF24.FLUSH_RX])[0] def flush_tx(self): return self.spidev.xfer2([NRF24.FLUSH_TX])[0] def get_status(self): return self.spidev.xfer2([NRF24.NOP])[0] def print_status(self, status): status_str = "STATUS\t = 0x{0:02x} RX_DR={1:x} TX_DS={2:x} MAX_RT={3:x} RX_P_NO={4:x} TX_FULL={5:x}".format( status, 1 if status & _BV(NRF24.RX_DR) else 0, 1 if status & _BV(NRF24.TX_DS) else 0, 1 if status & _BV(NRF24.MAX_RT) else 0, ((status >> NRF24.RX_P_NO) & 7), 1 if status & _BV(NRF24.TX_FULL) else 0) print (status_str) def print_observe_tx(self, value): print ("Observe Tx: %02x Lost Pkts: %d Retries: %d" % (value, value >> NRF24.PLOS_CNT, value & 15)) def print_byte_register(self, name, reg, qty=1): extra_tab = '\t' if len(name) < 8 else 0 print ("%s\t%c =" % (name, extra_tab)), while qty > 0: print ("0x%02x" % (self.read_register(reg))), qty -= 1 reg += 1 print ("") def print_address_register(self, name, reg, qty=1): extra_tab = '\t' if len(name) < 8 else 0 print ("%s\t%c =" % (name, extra_tab)), while qty > 0: qty -= 1 buf = reversed(self.read_register(reg, 5)) reg += 1 sys.stdout.write(" 0x"), for i in buf: sys.stdout.write("%02x" % i) print ("") def setChannel(self, channel): self.channel = min(max(0, channel), NRF24.MAX_CHANNEL) self.write_register(NRF24.RF_CH, self.channel) def getChannel(self): return self.read_register(NRF24.RF_CH) def setPayloadSize(self, size): self.payload_size = min(max(size, 1), NRF24.MAX_PAYLOAD_SIZE) def getPayloadSize(self): return self.payload_size def printDetails(self): self.print_status(self.get_status()) self.print_address_register("RX_ADDR_P0-1", NRF24.RX_ADDR_P0, 2) self.print_byte_register("RX_ADDR_P2-5", NRF24.RX_ADDR_P2, 4) self.print_address_register("TX_ADDR", NRF24.TX_ADDR) self.print_byte_register("RX_PW_P0-6", NRF24.RX_PW_P0, 6) self.print_byte_register("EN_AA", NRF24.EN_AA) self.print_byte_register("EN_RXADDR", NRF24.EN_RXADDR) self.print_byte_register("RF_CH", NRF24.RF_CH) self.print_byte_register("RF_SETUP", NRF24.RF_SETUP) self.print_byte_register("CONFIG", NRF24.CONFIG) self.print_byte_register("DYNPD/FEATURE", NRF24.DYNPD, 2) # print ("Data Rate\t = %s" % NRF24.datarate_e_str_P[self.getDataRate()]) print ("Model\t\t = %s" % NRF24.model_e_str_P[self.isPVariant()]) print ("CRC Length\t = %s" % NRF24.crclength_e_str_P[self.getCRCLength()]) print ("PA Power\t = %s" % NRF24.pa_dbm_e_str_P[self.getPALevel()]) def begin(self, csn_pin, ce_pin=0): # csn & ce are RF24 terminology. csn = SPI's CE! # Initialize SPI bus.. # ce_pin is for the rx=listen or tx=trigger pin on RF24 (they call that ce !!!) # CE optional (at least in some circumstances, eg fixed PTX PRX roles, no powerdown) # CE seems to hold itself as (sufficiently) HIGH, but tie HIGH is safer! self.spidev.open(0, csn_pin) self.spidev.max_speed_hz = 100000 self.ce_pin = ce_pin if ce_pin: self.GPIO.setup(self.ce_pin, self.GPIO.OUT) time.sleep(5 / 1000000.0) # Set 1500uS (minimum for 32B payload in ESB@250KBPS) timeouts, to make testing a little easier # WARNING: If this is ever lowered, either 250KBS mode with AA is broken or maximum packet # sizes must never be used. See documentation for a more complete explanation. self.write_register(NRF24.SETUP_RETR, (0b0100 << NRF24.ARD) | 0b1111) # Restore our default PA level self.setPALevel(NRF24.PA_MAX) # Determine if this is a p or non-p RF24 module and then # reset our data rate back to default value. This works # because a non-P variant won't allow the data rate to # be set to 250Kbps. if self.setDataRate(NRF24.BR_250KBPS): self.p_variant = True # Then set the data rate to the slowest (and most reliable) speed supported by all # hardware. self.setDataRate(NRF24.BR_1MBPS) # Initialize CRC and request 2-byte (16bit) CRC self.setCRCLength(NRF24.CRC_16) # Disable dynamic payloads, to match dynamic_payloads_enabled setting self.write_register(NRF24.DYNPD, 0) # Reset current status # Notice reset and flush is the last thing we do self.write_register(NRF24.STATUS, _BV(NRF24.RX_DR) | _BV(NRF24.TX_DS) | _BV(NRF24.MAX_RT)) # Set up default configuration. Callers can always change it later. # This channel should be universally safe and not bleed over into adjacent # spectrum. self.setChannel(self.channel) # Flush buffers self.flush_rx() self.flush_tx() def end(self): if self.spidev: self.spidev.close() self.spidev = None def startListening(self): self.write_register(NRF24.CONFIG, self.read_register(NRF24.CONFIG) | _BV(NRF24.PWR_UP) | _BV(NRF24.PRIM_RX)) self.write_register(NRF24.STATUS, _BV(NRF24.RX_DR) | _BV(NRF24.TX_DS) | _BV(NRF24.MAX_RT)) # Restore the pipe0 address, if exists if self.pipe0_reading_address: self.write_register(self.RX_ADDR_P0, self.pipe0_reading_address, 5) # Go! self.ce(NRF24.HIGH) # wait for the radio to come up (130us actually only needed) time.sleep(130 / 1000000.0) def stopListening(self): self.ce(NRF24.LOW) self.flush_tx() self.flush_rx() def powerDown(self): self.write_register(NRF24.CONFIG, self.read_register(NRF24.CONFIG) & ~_BV(NRF24.PWR_UP)) def powerUp(self): self.write_register(NRF24.CONFIG, self.read_register(NRF24.CONFIG) | _BV(NRF24.PWR_UP)) time.sleep(150 / 1000000.0) def write(self, buf): # Begin the write self.startWrite(buf) timeout = self.getMaxTimeout() #s to wait for timeout sent_at = time.time() while True: #status = self.read_register(NRF24.OBSERVE_TX, 1) status = self.get_status() if (status & (_BV(NRF24.TX_DS) | _BV(NRF24.MAX_RT))) or (time.time() - sent_at > timeout ): break time.sleep(10 / 1000000.0) #obs = self.read_register(NRF24.OBSERVE_TX) #self.print_observe_tx(obs) #self.print_status(status) # (for debugging) what = self.whatHappened() result = what['tx_ok'] if what['tx_fail']: self.flush_tx(); # bl - dont jam up the fifo # Handle the ack packet if what['rx_ready']: self.ack_payload_length = self.getDynamicPayloadSize() self.ack_payload_available = True ## bl return result def startWrite(self, buf): # Transmitter power-up self.write_register(NRF24.CONFIG, (self.read_register(NRF24.CONFIG) | _BV(NRF24.PWR_UP) ) & ~_BV(NRF24.PRIM_RX)) # Send the payload self.write_payload(buf) # Allons! if self.ce_pin: if self.GPIO.RPI_REVISION > 0: self.ce(self.GPIO.HIGH) time.sleep(10 / 1000000.0) self.ce(self.GPIO.LOW) else: # virtGPIO is slower. A 10 uSec pulse is better done with pulseOut(): self.GPIO.pulseOut(self.ce_pin, self.GPIO.HIGH, 10) def getDynamicPayloadSize(self): return self.spidev.xfer2([NRF24.R_RX_PL_WID, NRF24.NOP])[1] def available(self, pipe_num=None): if not pipe_num: pipe_num = [] status = self.get_status() result = False # Sometimes the radio specifies that there is data in one pipe but # doesn't set the RX flag... if status & _BV(NRF24.RX_DR) or (status & 0b00001110 != 0b00001110): result = True if result: # If the caller wants the pipe number, include that if len(pipe_num) >= 1: pipe_num[0] = ( status >> NRF24.RX_P_NO ) & 0b00000111 # Clear the status bit # ??? Should this REALLY be cleared now? Or wait until we # actually READ the payload? self.write_register(NRF24.STATUS, _BV(NRF24.RX_DR)) # Handle ack payload receipt if status & _BV(NRF24.TX_DS): self.write_register(NRF24.STATUS, _BV(NRF24.TX_DS)) return result def read(self, buf, buf_len=-1): # Fetch the payload self.read_payload(buf, buf_len) # was this the last of the data available? return self.read_register(NRF24.FIFO_STATUS) & _BV(NRF24.RX_EMPTY) def whatHappened(self): # Read the status & reset the status in one easy call # Or is that such a good idea? status = self.write_register(NRF24.STATUS, _BV(NRF24.RX_DR) | _BV(NRF24.TX_DS) | _BV(NRF24.MAX_RT)) # Report to the user what happened tx_ok = status & _BV(NRF24.TX_DS) tx_fail = status & _BV(NRF24.MAX_RT) rx_ready = status & _BV(NRF24.RX_DR) return {'tx_ok': tx_ok, "tx_fail": tx_fail, "rx_ready": rx_ready} def openWritingPipe(self, value): # Note that the NRF24L01(+) # expects it LSB first. self.write_register(NRF24.RX_ADDR_P0, value, 5) self.write_register(NRF24.TX_ADDR, value, 5) max_payload_size = 32 self.write_register(NRF24.RX_PW_P0, min(self.payload_size, max_payload_size)) def openReadingPipe(self, child, address): # If this is pipe 0, cache the address. This is needed because # openWritingPipe() will overwrite the pipe 0 address, so # startListening() will have to restore it. if child == 0: self.pipe0_reading_address = address if child <= 6: # For pipes 2-5, only write the LSB if child < 2: self.write_register(NRF24.child_pipe[child], address, 5) else: self.write_register(NRF24.child_pipe[child], address, 1) self.write_register(NRF24.child_payload_size[child], self.payload_size) # Note it would be more efficient to set all of the bits for all open # pipes at once. However, I thought it would make the calling code # more simple to do it this way. self.write_register(NRF24.EN_RXADDR, self.read_register(NRF24.EN_RXADDR) | _BV(NRF24.child_pipe_enable[child])) def closeReadingPipe(self, pipe): self.write_register(NRF24.EN_RXADDR, self.read_register(EN_RXADDR) & ~_BV(NRF24.child_pipe_enable[pipe])) def toggle_features(self): buf = [NRF24.ACTIVATE, 0x73] self.spidev.xfer2(buf) def enableDynamicPayloads(self): # Enable dynamic payload throughout the system self.write_register(NRF24.FEATURE, self.read_register(NRF24.FEATURE) | _BV(NRF24.EN_DPL)) # If it didn't work, the features are not enabled if not self.read_register(NRF24.FEATURE): # So enable them and try again self.toggle_features() self.write_register(NRF24.FEATURE, self.read_register(NRF24.FEATURE) | _BV(NRF24.EN_DPL)) # Enable dynamic payload on all pipes # Not sure the use case of only having dynamic payload on certain # pipes, so the library does not support it. self.write_register(NRF24.DYNPD, self.read_register(NRF24.DYNPD) | _BV(NRF24.DPL_P5) | _BV(NRF24.DPL_P4) | _BV( NRF24.DPL_P3) | _BV(NRF24.DPL_P2) | _BV(NRF24.DPL_P1) | _BV(NRF24.DPL_P0)) self.dynamic_payloads_enabled = True def enableAckPayload(self): # enable ack payload and dynamic payload features self.write_register(NRF24.FEATURE, self.read_register(NRF24.FEATURE) | _BV(NRF24.EN_ACK_PAY) | _BV(NRF24.EN_DPL)) # If it didn't work, the features are not enabled if not self.read_register(NRF24.FEATURE): # So enable them and try again self.toggle_features() self.write_register(NRF24.FEATURE, self.read_register(NRF24.FEATURE) | _BV(NRF24.EN_ACK_PAY) | _BV(NRF24.EN_DPL)) # Enable dynamic payload on pipes 0 & 1 self.write_register(NRF24.DYNPD, self.read_register(NRF24.DYNPD) | _BV(NRF24.DPL_P1) | _BV(NRF24.DPL_P0)) def writeAckPayload(self, pipe, buf, buf_len): txbuffer = [NRF24.W_ACK_PAYLOAD | ( pipe & 0x7 )] max_payload_size = 32 data_len = min(buf_len, max_payload_size) txbuffer.extend(buf[0:data_len]) self.spidev.xfer2(txbuffer) def isAckPayloadAvailable(self): result = self.ack_payload_available self.ack_payload_available = False return result def isPVariant(self): return self.p_variant def setAutoAck(self, enable): if enable: self.write_register(NRF24.EN_AA, 0b111111) else: self.write_register(NRF24.EN_AA, 0) def setAutoAckPipe(self, pipe, enable): if pipe <= 6: en_aa = self.read_register(NRF24.EN_AA) if enable: en_aa |= _BV(pipe) else: en_aa &= ~_BV(pipe) self.write_register(NRF24.EN_AA, en_aa) def testCarrier(self): return self.read_register(NRF24.CD) & 1 def testRPD(self): return self.read_register(NRF24.RPD) & 1 def setPALevel(self, level): setup = self.read_register(NRF24.RF_SETUP) setup &= ~( _BV(NRF24.RF_PWR_LOW) | _BV(NRF24.RF_PWR_HIGH)) # switch uses RAM (evil!) if level == NRF24.PA_MAX: setup |= (_BV(NRF24.RF_PWR_LOW) | _BV(NRF24.RF_PWR_HIGH)) elif level == NRF24.PA_HIGH: setup |= _BV(NRF24.RF_PWR_HIGH) elif level == NRF24.PA_LOW: setup |= _BV(NRF24.RF_PWR_LOW) elif level == NRF24.PA_MIN: nop = 0 elif level == NRF24.PA_ERROR: # On error, go to maximum PA setup |= (_BV(NRF24.RF_PWR_LOW) | _BV(NRF24.RF_PWR_HIGH)) self.write_register(NRF24.RF_SETUP, setup) def getPALevel(self): power = self.read_register(NRF24.RF_SETUP) & (_BV(NRF24.RF_PWR_LOW) | _BV(NRF24.RF_PWR_HIGH)) if power == (_BV(NRF24.RF_PWR_LOW) | _BV(NRF24.RF_PWR_HIGH)): return NRF24.PA_MAX elif power == _BV(NRF24.RF_PWR_HIGH): return NRF24.PA_HIGH elif power == _BV(NRF24.RF_PWR_LOW): return NRF24.PA_LOW else: return NRF24.PA_MIN def setDataRate(self, speed): result = False setup = self.read_register(NRF24.RF_SETUP) # HIGH and LOW '00' is 1Mbs - our default self.wide_band = False setup &= ~(_BV(NRF24.RF_DR_LOW) | _BV(NRF24.RF_DR_HIGH)) if speed == NRF24.BR_250KBPS: # Must set the RF_DR_LOW to 1 RF_DR_HIGH (used to be RF_DR) is already 0 # Making it '10'. self.wide_band = False setup |= _BV(NRF24.RF_DR_LOW) else: # Set 2Mbs, RF_DR (RF_DR_HIGH) is set 1 # Making it '01' if speed == NRF24.BR_2MBPS: self.wide_band = True setup |= _BV(NRF24.RF_DR_HIGH) else: # 1Mbs self.wide_band = False self.write_register(NRF24.RF_SETUP, setup) # Verify our result if self.read_register(NRF24.RF_SETUP) == setup: result = True else: self.wide_band = False return result def getDataRate(self): dr = self.read_register(NRF24.RF_SETUP) & (_BV(NRF24.RF_DR_LOW) | _BV(NRF24.RF_DR_HIGH)) # Order matters in our case below if dr == _BV(NRF24.RF_DR_LOW): # '10' = 250KBPS return NRF24.BR_250KBPS elif dr == _BV(NRF24.RF_DR_HIGH): # '01' = 2MBPS return NRF24.BR_2MBPS else: # '00' = 1MBPS return NRF24.BR_1MBPS def setCRCLength(self, length): config = self.read_register(NRF24.CONFIG) & ~( _BV(NRF24.CRC_16) | _BV(NRF24.CRC_ENABLED)) if length == NRF24.CRC_DISABLED: # Do nothing, we turned it off above. self.write_register(NRF24.CONFIG, config) return elif length == NRF24.CRC_8: config |= _BV(NRF24.CRC_ENABLED) config |= _BV(NRF24.CRC_8) else: config |= _BV(NRF24.CRC_ENABLED) config |= _BV(NRF24.CRC_16) self.write_register(NRF24.CONFIG, config) def getCRCLength(self): result = NRF24.CRC_DISABLED config = self.read_register(NRF24.CONFIG) & ( _BV(NRF24.CRCO) | _BV(NRF24.EN_CRC)) if config & _BV(NRF24.EN_CRC): if config & _BV(NRF24.CRCO): result = NRF24.CRC_16 else: result = NRF24.CRC_8 return result def disableCRC(self): disable = self.read_register(NRF24.CONFIG) & ~_BV(NRF24.EN_CRC) self.write_register(NRF24.CONFIG, disable) def setRetries(self, delay, count): # see specs. Delay code below 5 can conflict with some ACK lengths # and count should be set = 0 for non-ACK modes self.write_register(NRF24.SETUP_RETR, (delay & 0xf) << NRF24.ARD | (count & 0xf)) def getRetries(self): return self.read_register(NRF24.SETUP_RETR) def getMaxTimeout(self): # seconds retries = self.getRetries() tout = (((250+(250*((retries& 0xf0)>>4 ))) * (retries & 0x0f)) / 1000000.0 * 2) + 0.008 # Fudged up to about double Barraca's calculation # Was too short & was timeing out wrongly. BL return tout