Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Closed
davidalcoba opened this issue Apr 12, 2017 · 8 comments
Closed

Comments

@davidalcoba
Copy link

davidalcoba commented Apr 12, 2017

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:

  • 1 Arduino (transmitter) - 1 Raspberry Pi (receiver): the data from the arduino is transmitted to the receiver and the information written in the ack payload is well displayed in the arduino LCD screen. It works fine independently of the Arduino that is running.

  • 2 Arduinos (transmitters) - 1 Raspberry Pi (receiver): the data from the arduino is transmitted to the receiver (the packages arrive and are logged along with the pipe id from which they come from) but the ack payload doesn't reach the transmitter, or at least not always. It is like there were collisions between the acks or something similar. If one arduino is disconnected then the other starts to receive the acks as usual, even though sometimes (i still haven't found a pattern) the throughput of the received ack packets decreases dramatically than being on the previous scenario.

Some code (simplified):

  • Transmitter (Arduino)
(...)
//first byte is 0xb2 or 0xb3 depending on the arduino
const unsigned char ADDRESS[5]  = {0xb3,0x43,0x88,0x99,0x45}; 

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
Copy link
Contributor

wmarkow commented Apr 12, 2017

Two things that I have related to your transmitter code:

  • you try to send the data constantly in the loop, over and over. For one transmitter it would be ok but when you have two transmitters the probability of the collision gets higher. Try to put one second of silence (no transmission) before you send 'X' to the receiver.
  • the auto-ack is enabled and the auto retransmission is enabled as well (in RF24 library by default). If the receiver doesn't get the auto-ACK then it re-sends the packet again (it tries to resend it for 15 times). The probability of packets collision increases when you send your data constantly.

@davidalcoba
Copy link
Author

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

In the meanwhile just a couple of things:

  • What are exactly the collisions you are referring to?
  • The whole system is a real-time application so delays between writes might not be feasible. Is the rf24 communication still valid on this scenario?

Thanks!!!

@wmarkow
Copy link
Contributor

wmarkow commented Apr 13, 2017

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
Copy link
Author

davidalcoba commented Apr 13, 2017 via email

@wmarkow
Copy link
Contributor

wmarkow commented Apr 13, 2017

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
Copy link
Author

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.

@TMRh20 TMRh20 closed this as completed May 10, 2019
@SerialProgrammer
Copy link

SerialProgrammer commented Jul 1, 2021

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
Copy link

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?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants