dparson55 / NRFLite

nRF24L01+ library with AVR 2 pin support, requiring very little code along with YouTube videos showing all available features. It can be installed via the Arduino IDE Library Manager
MIT License
155 stars 25 forks source link

Compatibility with RF24 library #54

Closed tong67 closed 3 years ago

tong67 commented 3 years ago

I needed multiple free pins with ATtiny85 so used the NRFLite 2 pins solution for Rx. The Tx needed to be run in conjunction with a RPI which is supported with nRF24 library (https://github.com/nRF24/RF24). To used the default of NRFLite, the RF24 has to configured with set of settings. This combines advantages of both worlds. It might be usefull for other users as well so might be added to the readme. Required settings:

const uint64_t pipes[2] = { 0x0004030201LL, 0x0104030201LL }; radio.setChannel(100); // channel 100 radio.setPALevel(RF24_PA_MAX); // MAX power radio.setDataRate(RF24_2MBPS); // 2MBPS radio.setRetries(1, 15); // 250+1*250us delay, 15 retries radio.enableDynamicAck(); // Enable auto ack radio.enableAckPayload(); // enable payload with ack and dynamic payload length radio.setCRCLength(RF24_CRC_8); // CRC8 radio.openWritingPipe(pipes[0]); radio.openReadingPipe(1, pipes[1]); radio.write(&_radioData, sizeof(_radioData), true)

Attached sketch of Basic_TX based on RF24 library which works with Basic_RX. Same Basic_TX but now as python script run on RPI

Basic_TX_RF24.ino.txt Basic_TX.py.txt

dparson55 commented 3 years ago

Thanks for sharing this information. I'd love to add this into the library and wondered if you could provide detail on how to come up with the pipe addresses since I'm sure people would like that information.

Also, for the RPi connection to the radio did the pin numbers detailed on http://tmrh20.github.io/RF24/ work ok?

Radio RPi
MISO 21
MOSI 19
SCK 23
CE 25 (seems to be used in your example)
CSN 24
IRQ Not used
tong67 commented 3 years ago

Address information

Basic_TX

const static uint8_t RADIO_ID = 1; // Our radio's id. const static uint8_t DESTINATION_RADIO_ID = 0; // Id of the radio we will transmit to. _radio.init(RADIO_ID, PIN_RADIO_CE, PIN_RADIO_CSN) _radio.send(DESTINATION_RADIO_ID, &_radioData, sizeof(_radioData))

printDetails( ) shows: RX_ADDR_P1 1,2,3,4,1

Basic_RX

const static uint8_t RADIO_ID = 0; // Our radio's id. The transmitter will send to this id. _radio.init(RADIO_ID, PIN_RADIO_CE, PIN_RADIO_CSN) _radio.readData(&_radioData)

printDetails( ) shows: RX_ADDR_P1 1,2,3,4,0

RF24_TX

const static uint8_t RADIO_ID = 0; // Our radio's id. The transmitter will send to this id. _radio.init(RADIO_ID, PIN_RADIO_CE, PIN_RADIO_CSN) const uint64_t pipes[2] = { 0x0004030201LL, 0x0104030201LL }; radio.openWritingPipe(pipes[0]); radio.openReadingPipe(1, pipes[1]);

printDetails( ) shows: RX_ADDR_P0-1 = 0x0004030201 0x0104030201

Be aware of address differences: NRFLite from left to right and only last changeble: NRFLite send: 1,2,3,4, <1st argument init())>. Only last value is configurable NRFLite readDate: 1,2,3,4, <1st argument init())>. Only last value is configurable

RF24 from right to left. First (most left one) correspond with configurable value in NRFLite RF24 openWritingPipe: 0x04030201 RF24 openReadingPipe: 0x04030201

RPI used pins and relation with python script/output

The RPI pin numbering I used in the python file is not header pin 25 but the GPIO 25 which corresponds with header pin 22. radio = RF24(25,0) # CE, CSN: 25 (GPIO25) == header pin 22, 0 (CE0) == header pin 24, When set to 1 (CE1) CSN connects to header pin 26.

At startup of the python file CSN/CE and used addresses is reported: ================ SPI Configuration ================ CSN Pin = 0 CE Pin = Custom GPIO25 SPI Speedz = 10 Mhz ================ NRF Configuration ================ STATUS = 0x0e RX_DR=0 TX_DS=0 MAX_RT=0 RX_P_NO=7 TX_FULL=0 RX_ADDR_P0-1 = 0x0004030201 0x0104030201 RX_ADDR_P2-5 = 0xc3 0xc4 0xc5 0xc6 TX_ADDR = 0x0004030201

dparson55 commented 3 years ago

Doug asks:

I have looked around and can't find any examples of using NRFLite as a TX to a receiver running NRF24/NRF24L01 and have not been able to get this working. If you have an example or pointer to some code, I would appreciate it. I've tried matching channel, speed, addressing and am limited in my knowledge of C and the NRF. It could be the dynamic payload or ACK settings that I'm not matching. The pipes addressing is also confusing to me given other examples vs. the "1,2,3,4, radioID" that I see used.

Any suggestions?

Take a look at the files @tong67 provided above. He figured out how to make the micropython version of the RF24 library transmit to a receiver running NRFLite, so you could use it as a starting point for reversing the direction. Unfortunately I don't have an ESP to experiment with so don't have the exact code you need, but you'll see the addresses, dynamic payload, ack settings, retries, and the like are all being setup in his code.

The addresses might be the most complicated part. You have to convert the "1, 2, 3, 4, radioId" to hex and reverse the order of each value to get an equivalent address for the RF24 library. Here is an example.

NRFLite internal address array format = {1, 2, 3, 4, radioId} NRFLite radio Id 0 address array = {1, 2, 3, 4, 0} Hex values = 01, 02, 03, 04, 00 Reversed hex values = 00, 04, 03, 02, 01 RF24 address in C = 0x0004030201LL RF24 address in micropython = 0x0004030201

On the receiver side, place the address into pipe 1 and on the NRFLite transmitter side, send data to the equivalent radio Id of that address. This is my best guess for micropython:

# Initialize radio with settings used by NRFLite
radio.setChannel(100)          # channel 100
radio.setDataRate(RF24_2MBPS)  # 2MBPS
radio.setPALevel(RF24_PA_MAX)  # MAX power
radio.setRetries(1, 15)        # 250+1*250us delay, 15 retries
radio.enableDynamicAck()       # Enable auto ack
radio.enableAckPayload()       # enable payload with ack and dynamic payload length
radio.setCRCLength(RF24_CRC_8) # CRC8

# Start listening for packets sent by a NRFLite transmitter that is sending packets to radio Id 0
rx_pipe = 1
rx_address = 0x0004030201
radio.openReadingPipe(rx_pipe, rx_address) 
DougWilkinson commented 3 years ago

Hi, I've tried matching the code example from tong67, still without success... I included the main receiving code, address is formatted for radio "0", speed is 2M, channel 100. I think this is correct. I also modified the NRF24L01 to match the other settings (see further below)

` import sys import ustruct as struct import utime from machine import Pin, SPI from nrf24l01 import NRF24L01 from micropython import const

cfg = {"spi": 1, "miso": 12, "mosi": 13, "sck": 14, "csn": 4, "ce": 2} pipes = (b"\x00\x04\x03\x02\x01", b"\x04\x04\x03\x02\x01")

csn = Pin(cfg["csn"], mode=Pin.OUT, value=1) ce = Pin(cfg["ce"], mode=Pin.OUT, value=0)

print("Initializing NRF ...") nrf = NRF24L01(SPI(cfg["spi"]), csn, ce, channel=100, payload_size=32)

nrf.open_rx_pipe(1, pipes[0]) nrf.start_listening()

print("NRFlite receive test, waiting for packets... (ctrl-C to stop)")

while True: if nrf.any(): while nrf.any(): buf = nrf.recv() print("received:", buf) `

Modified NRF24L01 imported above: I won't paste everything, just the parts I changed or seem relevant:

` class NRF24L01: def init(self, spi, cs, ce, channel=100, payload_size=32, speed=SPEED_2M, power=POWER_3): assert payload_size <= 32

    # set address width to 5 bytes and check for device present
    self.reg_write(SETUP_AW, 0b11)
    if self.reg_read(SETUP_AW) != 0b11:
        raise OSError("nRF24L01+ Hardware not responding")

    #Enable dynamically sized payloads, ACK payloads,
    #and TX support with or without an ACK request.
    #writeRegister(FEATURE, _BV(EN_DPL) | _BV(EN_ACK_PAY) | _BV(EN_DYN_ACK));
    **self.reg_write(FEATURE, EN_DPL | EN_ACK_PAY | EN_DYN_ACK)**

    # enable dynamic payloads for P0 and P1
    # writeRegister(DYNPD, _BV(DPL_P0) | _BV(DPL_P1)); bits 0 and 1 on = 3
    **self.reg_write(DYNPD, 3)**

    # auto retransmit delay: 1750us - **NOT RELEVANT FOR RX ONLY?**
    # auto retransmit count: 8
    self.reg_write(SETUP_RETR, (6 << 4) | 8)

    # set rf power and speed
    self.set_power_speed(power, speed)  # Best for point to point links

    # init CRC (CRC8 just enable EN_CRC) - see call to set_crc below
    **self.set_crc(1)**

    # clear status flags
    self.reg_write(STATUS, RX_DR | TX_DS | MAX_RT)

    # set channel
    self.set_channel(channel)

# length in bytes: 0, 1 or 2
def set_crc(self, length):
    config = self.reg_read(CONFIG) & ~(CRCO | EN_CRC)
    if length == 0:
        pass
    elif length == 1:
        config |= EN_CRC
    else:
        config |= EN_CRC | CRCO
    self.reg_write(CONFIG, config)

` This still doesn't work and I think I've matched the settings. Is the transmit retry needed for my receiver? I might just try changing some of the NRFlite settings and recompile to see if I can match the micropython instead. Maybe I am missing a default that is somehow getting set without an explicit register setting? Thanks for any suggestions! --Doug

dparson55 commented 3 years ago

I don't believe the retry settings are needed on the RX side but am not 100% certain since I have never tested things without it.

Is there a way for you to print out all the nRF24L01's registers? That might make it easier to identify the problem.

DougWilkinson commented 3 years ago

I can probably figure some way to do this in micropython. I'd like to match the output from the printDetails(). How do I enable debug/Serial output for NRFlite? it looks like there are two #define statements related to _serial, but my C++/Arduino skills are rusty. Thanks! --Doug

dparson55 commented 3 years ago

When you create the radio object, pass in a Serial object as mentioned here and there's an example of this on ChannelScanner line 33.

printDetails calls printRegister which references the debugln define which ultimately uses this serial object.

DougWilkinson commented 3 years ago

Thank you for the pointer on setting the Serial up. Finally success! I managed to dump registers from an NRFLite and micropython RCVR instance to compare. Initially there were a few minor register differences, but these are what I am using now from micropython:

CONFIG : 0b1011 EN_AA : 0b111111 EN_RXADDR : 0b11 SETUP_AW : 0b11 SETUP_RETR : 0b11111 RF_CH : 0b1100100 RF_SETUP : 0b1110 STATUS : 0b1110 OBSERVE_TX : 0b0 RX_PW_P0 : 0b100000 RX_PW_P1 : 0b100000 FIFO_STATUS : 0b10001 DYNPD : 0b11 FEATURE : 0b111 TX_ADDR : b'\x04\x04\x03\x02\x01' RX_ADDR_P0 : b'\x04\x04\x03\x02\x01' RX_ADDR_P1 : b'\x00\x04\x03\x02\x01' compared to the registers on NRFlite: CONFIG 00001011 EN_AA 00111111 EN_RXADDR 00000011 SETUP_AW 00000011 SETUP_RETR 00011111 RF_CH 01100100 RF_SETUP 00001110 STATUS 00001110 OBSERVE_TX 00000000 RX_PW_P0 00000000 RX_PW_P1 00000000 FIFO_STATUS 00010001 DYNPD 00000011 FEATURE 00000111 TX_ADDR 231,231,231,231,231 RX_ADDR_P0 231,231,231,231,231 RX_ADDR_P1 1,2,3,4,0 This did not work until I changed the address ordering back to match the NRFlite:

open_rx_pipe(1,b"\x01\x02\x03\x04\x00")

I thought I had tried that, but to be honest, I went through so many iterations of things, I've lost track. The key here is to match the register settings and if in doubt, both address values (regular and little endian)

Thanks for helping me figure this out, I learned at lot about SPI and the NRF module! --Doug

dparson55 commented 3 years ago

Sorry for the delay but congrats on getting things to work. Hope the rest of the project is a little easier and happy holidays!