nRF24 / RF24

OSI Layer 2 driver for nRF24L01 on Arduino & Raspberry Pi/Linux Devices
https://nrf24.github.io/RF24
GNU General Public License v2.0
2.21k stars 1.02k forks source link

Sending back ACK payloads from one receiver to multiple transmitters #351

Closed davidalcoba closed 5 years ago

davidalcoba commented 7 years ago

Hi,

I have a scenario with multiple transmitters (arduino micro) and one single receiver (raspberry pi/python). All the transmitters send data got from their sensors to the receiver who consolidates the information and send back some aggregated information to each one that is displayed on a LCD screen (each transmitter has its own LCD screen).

Since only one writing channel is allowed I decided to use the ACK payload functionality so the consolidated information is sent back to the transmitters within the acknowledge payload.

Currently I have two arduinos that transmit data to the raspberry by two different addresses (channels 2 and 3 in the rpi radio). I have the expected behaviour when just one arduino is running, but not when two of them are running at the same time:

Some code (simplified):

void setup() { lcdInit(); rf24init(); }

void lcdInit() {...}

void rf24init() { radio.begin(); radio.setAutoAck(1); radio.enableDynamicPayloads(); radio.enableAckPayload(); radio.setPALevel(RF24_PA_MAX); radio.openWritingPipe(ADDRESS); radio.stopListening(); }

void loop() { byte msgOut[1] = { 'X' }; //simplified example with only 1-byte message if (radio.write(&msgOut, sizeof(msgOut)) ) { while (radio.isAckPayloadAvailable()) { int32_t msgSize = radio.getDynamicPayloadSize(); //ack payload length is dynamic byte msgIn[msgSize]; radio.read(&msgIn, msgSize); lcdScreenMgr.write(msgIn); } }

(...)


- Receiver (Raspberry Pi / python):

(...) def initRadio():

not sure if ADDRESS0 and ADDRESS1 are needed

ADDRESS0 = [0xb0,0x43,0x88,0x99,0x45] ADDRESS1 = [0xb1,0x43,0x88,0x99,0x45] ADDRESS2 = [0xb2];
ADDRESS3 = [0xb3];

radio = RF24(25, 0) radio.begin() radio.setAutoAck(1) radio.enableDynamicPayloads() radio.enableAckPayload() radio.openReadingPipe(2,bytearray(ADDRESS2)); radio.openReadingPipe(3,bytearray(ADDRESS3)); radio.startListening()

return radio

def radio_comm_worker(radio): pipe = 0 while True: available = radio.available_pipe() if (available[0] > 0): print("Msg received from:" + str(available[1]))

simplified example with only 1-byte in message

  msgIn = radio.read(1)
  msgOut = do_some_stuff(msgIn)
  radio.writeAckPayload(available[1],bytearray(msgOut))

def main(argv): (...) radio = initRadio() t1 = threading.Thread(target=radio_comm_worker, args=[radio]) t1.daemon = True t1.start() (...)

Ctr+C exit

try: while True: time.sleep(1) except KeyboardInterrupt: sys.exit(1)

if name == 'main': main(sys.argv[1:])



Any suggestion on why acks are not correctly received by the arduinos? If the approach is wrong because of hardware limitations, is there any other way to send the "consolidated data" from the RPi to the Arduinos (considering `do_some_stuff(msgIn)` produces different outputs per transmitter and message)?

Thanks!
wmarkow commented 7 years ago

Two things that I have related to your transmitter code:

davidalcoba commented 7 years ago

Thanks for your quick answer, will try your suggestions asap and comment the results.

In the meanwhile just a couple of things:

Thanks!!!

wmarkow commented 7 years ago

What are exactly the collisions you are referring to?

Imagine three people standing somewhere. When one talks and the other listens, the voice transmission is not disturbed; the others can hear clearly. When two people talks at the same time, their voices interfere which each other and the third guy may have some "problems" with understanding the "voice transmission". The same thing (collisions) are when more that one RF24 chip are transmitting on the same channel at the same (more or less) time. Their packets collide which each other. The receiver is not able to distinguish which transmission comes from which transmitter as it receive the "sum" of two transmissions (which is collided then). Receiver gets the packet, checks it CRC and finds out that there is CRC mismatch and trashes the packet. If at least one device constantly transmits data then the probability of the collisions increases.

The whole system is a real-time application so delays between writes might not be feasible.

I was just referring to your example source code of the transmitter where you constantly try to write a byte. The idea is to not to transmit so often to lower the possibility of collisions. Give it a try and we will see. Remember that this RF24 chips have the auto retransmission mechanism enabled by default. It will try to resend the packet for 15 times until ACK is received.

Is the rf24 communication still valid on this scenario?

I think it could be valid. First you must define how many transmitters you have and how often they need to make a transmission. It may turn out that you need to implement some more sophisticated media access mechanism (like for example wait a random number of milliseconds before every transmission) to lower the collisions factor. Take a look at the ALOHAnet. This is not so easy.

davidalcoba commented 7 years ago

Thanks again Witold for your reply.

If I understand you correctly the collisions are produced because all the communications are overlapped within the same frequency so the receiver discards many packages because of bad CRCs. I thought this was precisely the intention of having different channels or addresses, but I guess this is just for identification purposes but what it really matters in regards to interferences is the frequency. Ok, however what I don't understand is why all the packages sent by different channels are arriving to the receiver (remember that I am logging them in the console) but the ACK don't reach the transmitter. It is like the radio module is using the same channel for sending the ACK back to the transmitters, and it is then when the collision is produced. Does it make sense?

I have added delays between writes and the problem remains. Actually the idea behind the real-time necessity is that I am planning to have a led matrix per transmitter instead of the lcd and I'd like to refresh the images as frequent as possible based on the ACK payload data.

I will add some more traces in both sides to see the ratio of packages lost in both sides. in case the problem is in the transmission of ACKs because they all share the same channel, is there any way to transmit from one radio (RPi) to multiple receivers? I guess I should play with the start/stopListening and openWritingPipe on demand for each receiver.

On 13 April 2017 at 14:28, Witold Markowski notifications@github.com wrote:

What are exactly the collisions you are referring to?

Imagine three people standing somewhere. When one talks and the other listens, the voice transmission is not disturbed; the others can hear clearly. When two people talks at the same time, their voices interfere which each other and the third guy may have some "problems" with understanding the "voice transmission". The same thing (collisions) are when more that one RF24 chip are transmitting on the same channel at the same (more or less) time. Their packets collide which each other. The receiver is not able to distinguish which transmission comes from which transmitter as it receive the "sum" of two transmissions (which is collided then). Receiver gets the packet, checks it CRC and finds out that there is CRC mismatch and trashes the packet. If at least one device constantly transmits data then the probability of the collisions increases.

The whole system is a real-time application so delays between writes might not be feasible.

I was just referring to your example source code of the transmitter where you constantly try to write a byte. The idea is to not to transmit so often to lower the possibility of collisions. Give it a try and we will see. Remember that this RF24 chips have the auto retransmission mechanism enabled by default. It will try to resend the packet for 15 times until ACK is received.

Is the rf24 communication still valid on this scenario?

I think it could be valid. First you must define how many transmitters you have and how often they need to make a transmission. It may turn out that you need to implement some more sophisticated media access mechanism (like for example wait a random number of milliseconds before every transmission) to lower the collisions factor. Take a look at the ALOHAnet https://en.wikipedia.org/wiki/ALOHAnet. This is not so easy.

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/nRF24/RF24/issues/351#issuecomment-293881588, or mute the thread https://github.com/notifications/unsubscribe-auth/AB2XdxIXFXHMoF9eO2A7ROp0Ir24y2wrks5rvhT4gaJpZM4M7o-y .

-- @davidalcoba

wmarkow commented 7 years ago

Channel and address are quite two different things. Channel is related to the transmission frequency; both chips need to have the same channel to communicate. In your case you have a default channel so your chips should be able to communicate. But collisions may happen.

Addresses are related to transmitted packets. Every chip can send data to one receiver only, but specific chip can read data from up to 6 different other chips. If the chip receives the packet which address is different that in configured pipes, then the packet is discarded (if I understand correctly).

Another thing I have found. I'm not familiar with Python but it could be that your receiver code doesn't work as it should:

pipe = 0
  while True:
    available = radio.available_pipe()
    if (available[0] > 0):
      print("Msg received from:" + str(available[1]))
      #simplified example with only 1-byte in message
      msgIn = radio.read(1)
      msgOut = do_some_stuff(msgIn)
      radio.writeAckPayload(available[1],bytearray(msgOut))

From what pipe are you trying to read and and where do you send response? This must be some Python stuff which I obviously do not understand :)

Maybe you could try to not use that enableAckPayload() then chip connected to rPi should send the ACK automatically and we will see if this is a Python script issue or collisions issue, or what?

I think the easiest test scenario could be: have two transmitters on on receiver. Transmitters sends data not so often (lets say one packet per second). Receiver sends auto-ack automatically (so ack-payload is disbled). Receiver read the data from the chip and print some debug info.

davidalcoba commented 7 years ago

Hi again Witold, and sorry for not answering before. I have been busy with less interesting things :)

Thanks for your explanation on addresses and channels, it's really clear for me now.

I followed your suggestion and tried with a simpler scenario. It turned out that the problem was as you said provoked by sending messages so often, and that adding some delay made the whole thing work. However I'm afraid that this is not a good solution because the refresh of the led matrix is then not acceptable.

Also, I have been thinking that maybe it was not really a problem of collisions but an overflow in the amount of packets managed by the receiver node (RPi). I have read that there is a FIFO in which up to three 32-byte buffers are used to manage the received data. So I was guessing that this could be the cause of the problem if the receiver is not fast enough to consume all the data from the buffers, causing the transmitters to fail.

So my next test will be to completely change the strategy and swap transmitters by receivers and vice versa. This way the RP would be the only transmitter andi will send the data to the nodes one by one (changing the writing pipe address), and the ack will contain the response from the receivers (arduinos). Since each receiver only receives data from one transmitter (RPi) they should have more time to handle the request.

I will post the results once I have them.

SerialProgrammer commented 3 years ago

Hello everybody! deivkk, did you manage to implement the new strategy? My project ran into the same problem for me. I also decided to resort to the method of changing pipes for each transmitter, but something does not work. If you managed to do this, could you show the cycle in which the pipes change?

SShattered commented 3 years ago

Hi again Witold, and sorry for not answering before. I have been busy with less interesting things :)

Thanks for your explanation on addresses and channels, it's really clear for me now.

I followed your suggestion and tried with a simpler scenario. It turned out that the problem was as you said provoked by sending messages so often, and that adding some delay made the whole thing work. However I'm afraid that this is not a good solution because the refresh of the led matrix is then not acceptable.

Also, I have been thinking that maybe it was not really a problem of collisions but an overflow in the amount of packets managed by the receiver node (RPi). I have read that there is a FIFO in which up to three 32-byte buffers are used to manage the received data. So I was guessing that this could be the cause of the problem if the receiver is not fast enough to consume all the data from the buffers, causing the transmitters to fail.

So my next test will be to completely change the strategy and swap transmitters by receivers and vice versa. This way the RP would be the only transmitter andi will send the data to the nodes one by one (changing the writing pipe address), and the ack will contain the response from the receivers (arduinos). Since each receiver only receives data from one transmitter (RPi) they should have more time to handle the request.

I will post the results once I have them.

Did you resolve the problem?