Fundemental Question for radio.sendWithRetry and radio.receiveDone[SOLVED]

Started by K1JOS, July 07, 2014, 02:58:30 PM

K1JOS

I am trying to work out the sketch for full duplex like transceiver setup (each Moteino sending and receiving with confirmations AND nothing new can be sent until the received payload is confirmed. 
If one Moteino sends a payload using "radio.sendWithRetry" and the other Moteino is receiving using the "radio.receiveDone",  does the receiving Moteino also have to include code for the ACK using the " (radio.ACK_REQUESTED) function or do these specific send and recieve ve functions  handle the ACK in the background and only return true or false?

Felix

sendWithRetry() is on the SENDER side. On the receiving side you are responsible to check if ACK was requested (via radio.ACK_REQUESTED) and send one back when TRUE. Doesn't make sense to send an ACK using sendWithRetry (ie request ACK for an ACK).

K1JOS

Thats what I thought.  I don't want a stream of data flow in either direction  (Commands from Gateway to Node, and various data from Node to Gateway, to get out of sync.  So I am setting a  flag on both Moteinos in alternate fashion to determine whether its clear to send or receive either a new command (Gate -> Node) only after an ACK has been received from the last respective command/data transmission.  If my loop is too fast and I call 'radio.receiveDone before the other Moteino has responded, I assume there is a buffer that stores the payload until the next payload is received? 

thanks in advance

 

K1JOS

Felix,  One more question for clarity.  Do I put ' radio.ACK_REQUESTED' as a subroutine under 'radio.receiveDone" or as a separate routine outside of 'radio.receiveDone"?  I assume that if an ACK is requested and received that FIRST 'radio.receiveDone" must be true and then to test if ACK requested?

Felix

Please look at how the send/receive examples are structured: https://github.com/LowPowerLab/RFM69/tree/master/Examples
radio.ACK_REQUESTED just means that the last packet received had the ACK flag set.
radio.receiveDone() means a packet was received and it should be called whenever a packet is expected to arrive (as often as possible when they are expected any time).

K1JOS

I have spent many hours with the examples.  My fundmental problem is i need one Moteino to send specific commands requests (which sensor to select) and the other Moteino to receive the command requests and then send out the specific sensor data.  I tried to set this up using specific send and receive flags, but this always seems to easily get out of sync and then both are sending at same time and miss reception on both ends.  Am I correct that the RFM69 is not designed for full duplex activity?

The scheme I want to implement but just cannot get working is:

1) NODEID2 is waiting for payload

2) NODEID 1 sends a command (within Payload structure) and requests ACK.  IF ACK not received it sends it out command Payload again until ACK received.  When ACK received it sets receive_only_flag to true (receive only).

3) NODEID2 receives command and returns ACK.  Next it Sends out the command requested sensor data stream continuously until new command is received.

4) NODEID 1 receives specified data from NODEID2

5) NODEID 1 has a pushbutton interrupt (multiplexed on pin 3 using pin 4 and 5 with diodes - after interrupt I check whether 4 or 5 was pushed) to send a new command to NODEID 2


Here are some of my problems despite having the signal strength at -28 dB.

In Step 2 above ACK is very intermittently received.  I thought this was a timing issue with NODEID 1 sending command request continuously not giving NODEID2 time to send an ACK back.  But even putting in delays doesn't appear to help.   I tried While statements to keep radioreciving until ACK but this puts me in endless loops.

In Step 3 if NODEID 2 finally responds and sends a data stream back continuously, it misses any new Payload commands being sent to it by NODEID1.  Again I think this is a timing simplex problem that its sending data repeatedly from the loop that it has not time to listen. 

Any advice appreciated as I have been spending numerous hours of frustration trying to get this to work smoothly.

best
jerry





Felix

Jerry,
I think you may just be missing some of the details how the radios are limited.
I understand you need to send both ways. So both radios need to listen. For that you want to avoid any delays. Remember ... delays are BAD when you want throughput.
The reason is because you want to be calling receiveDone as often as possible. If you spend time in other parts of your code you will be missing on packets and ACKs.
Time spent in delay() is wasted and the radios are half deaf. Meaning they might receive a packet but you're ignoring it and other packets will overwrite it.

Also another fundamental thing to remember is that in these radios there is 1 (ONE) carrier frequency for all radios on the same frequency band. So only 1 (ONE) radio can talk at the same time without there being collisions. That means you are correct, it's not duplex at all. Radios listen then talk (CSMA scheme). Each packet takes significant time (ms) to be transmitted, and significant time to be ACKd (also a regular packet with a flag set). So only so much stuff can be transmitted into a second, or else you need to increase bitrate. You have to keep that in mind and avoid collisions. If your channel is very busy then collisions are still possible or nodes might not have a chance to transmit with a high probability of reception. ACKs are also packets, they are not special at all and don't have special rights in the channel.
Hope this helps...

K1JOS

Thanks Felix.  I assumed this and thats why I am trying to use send and receive flags on both ends to make sure that both are not transmitting at same time.

(Q1) However with a remote Moteino sending sensor data continuously, what can be done to give it time to also listen for a new command?

In my void Loop, alternating send and receive functions seems the only method.

(Q2) How do I provide time in between to allow the radio.receiveDone function to start and finish receiving a new (Payload) command before it resumes transmitting a new data (payload)?

When I call 'radio.receiveDone' to see if true and it returns false because receive may only be partially completed

(Q3) What happens the next time I call 'radio.receiveDone'?  Is there a buffer that keep the payload packets in some organized sequence or is it just lost if I missed the right moment?

sorry for the many questions but it seems pretty fundmental to understand the Moteino's capabilities or limitations

best
jerry

Felix

Jerry - i know you mentioned you spent time with the examples. But I think your questions go right back to what the Send/Receive examples are doing. The Receive listens most of the time and occasionally sends a package to the Sender. The Sender sends often, but listens all the time in between. It counts time using millis(). Every 300ms or so it stops listening and quickly sends the next message, then resumes listening. If a message comes in from someone (Receiver) it spits it out to serial, checks if an ACK was requested and if so send back an ACK, then resumes listening. I think this is exactly what you're looking for.
The library uses an external interrupt to store any incoming packets in a buffer. The buffer can only store 1 packet at a time. That's why calling receiveDone() is important to determine if a packet was received and ready to be read from the buffer. There is no such thing as a partial packet.
You can provide time in between sends/receives by counting it, for instance with millis() or micros(). That's a matter of being familiar with C++/Arduino rather than the RFM69 library.

K1JOS

Felix,

I apologize if I sound thickheaded.  As you just stated, the examples are crystal clear for one Moteino being primarily a sender and the other Moteino being primarily a receiver.  I get that.  What I need is for both Moteino's to be equally listeners and senders at opposite moments.  Can you see some method using send and receive flags to achieve this?

Felix

No worries. I think the Send example should be a good starting point since it acts"mostly" as a sender but actually it spends more time listening than sending.
You don't need flags, I don't think. All you need is to grasp how send() and sendWithRetry() are different and why you'd use one vs the other for the sending. Then receiveDone() just needs to be called the rest of the time and you should be good. In between you check for the button press/interrupt or read the sensor (which should take on the order of a few ms). If you're spending something like 500ms or seconds reading a sensor or debouncing then I'm not surprised at all you're missing packets.

K1JOS

In the library example - Struct_send, why do you use a byte methods for reconstructing the received payload, rather than just using the payload structure variables:  theData.nodeId,  theData.uptime and theData.temp ? 
------

if (radio.receiveDone())
  {
    Serial.print('[');Serial.print(radio.SENDERID, DEC);Serial.print("] ");
    for (byte i = 0; i < radio.DATALEN; i++)
      Serial.print((char)radio.DATA);
    Serial.print("   [RX_RSSI:");Serial.print(radio.readRSSI());Serial.print("]");

Felix

Quote from: K1JOS on July 08, 2014, 03:39:01 PM
In the library example - Struct_send, why do you use a byte methods for reconstructing the received payload, rather than just using the payload structure variables:  theData.nodeId,  theData.uptime and theData.temp ?
You confused me. You mean in the receive? I think I do just that. I have no idea what "byte methods" you're referring to.

K1JOS

Hi Felix,

Sorry about any confusion.  In the struct_send and receive examples.  The payload is only being sent in one direction (and read using the payload structure) while in the reverse direction the only sending was a string text for the Ping ACK test and this was being read on the other end by a char byte by byte string reconstruction.  I think I have found my problem in that I was using the same payload structure (and size) to send different variables in both directions.  Instead I created two different structures of different sizes to send command instructions  in one direction and a differently named payload structure (and size) to send data back in the other direction. I am afraid to say it but so far it seems to working perfectly!  One drawback as you pointed out, whichever Moteino is primarily receiving should not have to much distraction tied up processing other subroutines.  SO I also so far have cut down a lot of the fancy touchscreen LCD updating I originally had worked out for the Moteino used as my base station.  Maybe once I get it all completely final, maybe I will try to have the base Moteino send the LCD work off to another Arduino via 2 wire if I have enough Moteino digital pins free at the end.

Many thanks again for pointing me in the right direction

jerry

Felix

Yes spending a lot of time doing something else other than calling radio.receiveDone() is prone to missing packets that might arrive at any moment.