Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: GlasgowEmbedded/glasgow
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 4d19c9a5ba75
Choose a base ref
...
head repository: GlasgowEmbedded/glasgow
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 1fe537e565ea
Choose a head ref
  • 3 commits
  • 1 file changed
  • 1 contributor

Commits on Jan 25, 2020

  1. applet.radio.nrf24l01: add ShockBurst support.

    Very useful for raw GMSK RX/TX.
    whitequark committed Jan 25, 2020
    Copy the full SHA
    ad16b27 View commit details
  2. applet.radio.nrf24l01: add repeat receive support.

    This also fixes a bug where the received packet would not be flushed
    from RX FIFO, which meant you could receive at most 5 packets before
    power cycling the device. Oops...
    whitequark committed Jan 25, 2020
    Copy the full SHA
    b1a862d View commit details
  3. Copy the full SHA
    1fe537e View commit details
Showing with 72 additions and 39 deletions.
  1. +72 −39 software/glasgow/applet/radio/nrf24l01/__init__.py
111 changes: 72 additions & 39 deletions software/glasgow/applet/radio/nrf24l01/__init__.py
Original file line number Diff line number Diff line change
@@ -106,9 +106,12 @@ class RadioNRF24L01Applet(GlasgowApplet, name="radio-nrf24l"):
Transmit and receive packets using the nRF24L01/nRF24L01+ RF PHY.
This applet allows configuring all channel and packet parameters, and provides basic transmit
and receive workflow with one pipe and automatic transaction handling (Enhanced ShockBurst).
It does not support multiple pipes, acknowledgement payloads, or disabling transaction
handling (ShockBurst mode).
and receive workflow. It supports Enhanced ShockBurst (new packet framing with automatic
transaction handling) with one pipe, as well as ShockBurst (old packet framing). It does not
support multiple pipes or acknowledgement payloads.
Note that in the CLI, the addresses are most significant byte first (the same as on-air order,
and reversed with regards to register access order.)
The pinout of a common 8-pin nRF24L01+ module is as follows (live bug view):
@@ -176,7 +179,9 @@ def channel(value):
"invalid channel: {} (choose from 0..125)".format(value))
return value
def address(value):
return bytes.fromhex(value)
# In our API, byte 0 is LSB (which matches the order of writes to registers).
# But in our CLI, byte 0 is MSB (which matches the on-air format).
return bytes(reversed(bytes.fromhex(value)))
def length(value):
value = int(value, 10)
if value not in range(33):
@@ -207,8 +212,11 @@ def payload(value):
help="set address width to WIDTH bytes (one of: 2 3 4 5)")
parser.add_argument(
"-C", "--crc-width", metavar="WIDTH", type=int, required=True,
choices=(1, 2),
help="set CRC width to WIDTH bytes (one of: 1 2)")
choices=(0, 1, 2),
help="set CRC width to WIDTH bytes (one of: 0 1 2)")
parser.add_argument(
"-L", "--compat-framing", default=False, action="store_true",
help="disable automatic transaction handling, for pre-L01 compatibility")
parser.add_argument(
"-d", "--dynamic-length", default=False, action="store_true",
help="enable dynamic payload length (L01+ only)")
@@ -245,8 +253,16 @@ def payload(value):
p_receive.add_argument(
"-l", "--length", metavar="LENGTH", type=length,
help="receive packet with length LENGTH (mutually exclusive with --dynamic-length)")
p_receive.add_argument(
"-R", "--repeat", default=False, action="store_true",
help="keep receiving packets until interrupted")

async def interact(self, device, args, nrf24l01_iface):
if args.crc_width == 0 and not args.compat_framing:
raise RadioNRF24L01Error("Automatic transaction handling requires CRC to be enabled")
if args.dynamic_length and args.compat_framing:
raise RadioNRF24L01Error("Dynamic length requires automatic transaction handling")

self.logger.info("using channel %d (%d MHz) at %d dBm and %d kbps",
args.channel, 2400 + args.channel, args.power, args.bandwidth)
rf_ch = args.channel
@@ -271,9 +287,12 @@ async def interact(self, device, args, nrf24l01_iface):
5: AW._5_BYTES,
}[args.address_width]
crco = {
0: CRCO._1_BYTE,
1: CRCO._1_BYTE,
2: CRCO._2_BYTES,
}[args.crc_width]
en_crc = args.crc_width > 0
en_aa = not args.compat_framing
en_dpl = args.dynamic_length
en_dyn_ack = hasattr(args, "no_ack") and args.no_ack

@@ -290,23 +309,28 @@ async def interact(self, device, args, nrf24l01_iface):
REG_SETUP_AW(AW=aw).to_int())

if args.operation == "transmit":
await nrf24l01_iface.write_register(ADDR_CONFIG,
REG_CONFIG(PRIM_RX=0, PWR_UP=1, CRCO=crco, EN_CRC=1).to_int())

if len(args.address) != args.address_width:
raise RadioNRF24L01Error("Length of address does not match address width")
await nrf24l01_iface.write_register_wide(ADDR_TX_ADDR, args.address)
await nrf24l01_iface.write_register(ADDR_EN_AA,
REG_EN_AA(ENAA_P0=1).to_int())
await nrf24l01_iface.write_register(ADDR_EN_RXADDR,
REG_EN_RXADDR(ERX_P0=1).to_int())
await nrf24l01_iface.write_register_wide(ADDR_RX_ADDR_Pn(0), args.address)
if en_aa:
await nrf24l01_iface.write_register(ADDR_EN_AA,
REG_EN_AA(ENAA_P0=1).to_int())
await nrf24l01_iface.write_register(ADDR_SETUP_RETR,
REG_SETUP_RETR(ARD=args.transmit_timeout // 250 - 1,
ARC=args.retransmit_count).to_int())
await nrf24l01_iface.write_register(ADDR_EN_RXADDR,
REG_EN_RXADDR(ERX_P0=1).to_int())
await nrf24l01_iface.write_register_wide(ADDR_RX_ADDR_Pn(0), args.address)
else:
await nrf24l01_iface.write_register(ADDR_EN_AA,
REG_EN_AA().to_int()) # disable on all pipes to release EN_CRC
await nrf24l01_iface.write_register(ADDR_SETUP_RETR,
REG_SETUP_RETR().to_int())
if en_dpl:
await nrf24l01_iface.write_register(ADDR_DYNPD,
REG_DYNPD(DPL_P0=1).to_int())
await nrf24l01_iface.write_register(ADDR_SETUP_RETR,
REG_SETUP_RETR(ARD=args.transmit_timeout // 250 - 1,
ARC=args.retransmit_count).to_int())
await nrf24l01_iface.write_register(ADDR_CONFIG,
REG_CONFIG(PRIM_RX=0, PWR_UP=1, CRCO=crco, EN_CRC=en_crc).to_int())

while True:
fifo_status = REG_FIFO_STATUS.from_int(
@@ -315,7 +339,8 @@ async def interact(self, device, args, nrf24l01_iface):
break
await nrf24l01_iface.flush_tx()

await nrf24l01_iface.write_tx_payload(args.payload, ack=not args.no_ack)
await nrf24l01_iface.write_tx_payload(args.payload,
ack=not args.compat_framing and not args.no_ack)

await nrf24l01_iface.write_register(ADDR_STATUS,
REG_STATUS(TX_DS=1, MAX_RT=1).to_int())
@@ -331,7 +356,7 @@ async def interact(self, device, args, nrf24l01_iface):
await nrf24l01_iface.disable()

if status.TX_DS:
if args.no_ack:
if args.no_ack or args.compat_framing:
self.logger.info("packet sent")
else:
self.logger.info("packet acknowledged")
@@ -344,13 +369,14 @@ async def interact(self, device, args, nrf24l01_iface):
self.logger.error("packet lost")

if args.operation == "receive":
await nrf24l01_iface.write_register(ADDR_CONFIG,
REG_CONFIG(PRIM_RX=1, PWR_UP=1, CRCO=crco, EN_CRC=1).to_int())

if len(args.address) != args.address_width:
raise RadioNRF24L01Error("Length of address does not match address width")
await nrf24l01_iface.write_register(ADDR_EN_AA,
REG_EN_AA(ENAA_P0=1).to_int())
if en_aa:
await nrf24l01_iface.write_register(ADDR_EN_AA,
REG_EN_AA(ENAA_P0=1).to_int())
else:
await nrf24l01_iface.write_register(ADDR_EN_AA,
REG_EN_AA().to_int()) # disable on all pipes to release EN_CRC
await nrf24l01_iface.write_register(ADDR_EN_RXADDR,
REG_EN_RXADDR(ERX_P0=1).to_int())
await nrf24l01_iface.write_register_wide(ADDR_RX_ADDR_Pn(0), args.address)
@@ -365,29 +391,36 @@ async def interact(self, device, args, nrf24l01_iface):
raise RadioNRF24L01Error(
"One of --dynamic-length or --length must be specified")
await nrf24l01_iface.write_register(ADDR_RX_PW_Pn(0), args.length)
await nrf24l01_iface.write_register(ADDR_CONFIG,
REG_CONFIG(PRIM_RX=1, PWR_UP=1, CRCO=crco, EN_CRC=en_crc).to_int())

await nrf24l01_iface.write_register(ADDR_STATUS,
REG_STATUS(RX_DR=1).to_int())
await nrf24l01_iface.enable()
try:
while True:
status = REG_STATUS.from_int(
await nrf24l01_iface.read_register(ADDR_STATUS))
if status.RX_DR:
assert status.RX_P_NO == 0
await nrf24l01_iface.write_register(ADDR_STATUS,
REG_STATUS(RX_DR=1).to_int())
while True:
status = REG_STATUS.from_int(
await nrf24l01_iface.read_register(ADDR_STATUS))
if status.RX_DR:
assert status.RX_P_NO == 0
break
await asyncio.sleep(0.010)

if en_dpl:
length = await nrf24l01_iface.read_rx_payload_length()
else:
length = args.length
payload = await nrf24l01_iface.read_rx_payload(length)
await nrf24l01_iface.flush_rx()

self.logger.info("packet received: %s", dump_hex(payload))

if not args.repeat:
break
await asyncio.sleep(0.010)
finally:
await nrf24l01_iface.disable()

if en_dpl:
length = await nrf24l01_iface.read_rx_payload_length()
else:
length = args.length
payload = await nrf24l01_iface.read_rx_payload(length)

self.logger.info("packet received: %s", dump_hex(payload))

# -------------------------------------------------------------------------------------------------

class RadioNRF24L01AppletTestCase(GlasgowAppletTestCase, applet=RadioNRF24L01Applet):