Trouble with two-way communication

Started by jrdoner, January 24, 2016, 10:46:59 PM

jrdoner

I have set up a simple example using nodes 1 and 2, with 2 transmitting data to 1.  That works fine, but I cannot seem to get a transmit path working in the other direction.  Does this require promiscuous mode, or some  secret handshake?

The radios initialize just fine.  My loop()s for these two programs are shown below.

void loop() 
{
  int txRSSI;
 
  
  if (radio.receiveDone())
  {
      Serial.print("Tx node: "); Serial.println(radio.SENDERID);
      Serial.print("message "); Serial.println((char*)radio.DATA);
      txRSSI = radio.RSSI;
      Serial.print("RSSI "); Serial.println(txRSSI);
      Serial.println();  
 
  
    wait(500);
    radio.send(1,"Got it", 6);
  }  
}



void loop() 
{
  String stuff;
  int extra;
  
  timePassed = timePassed + 2;
  extra = timePassed + 3;
  
  sprintf(buff, "Init %d, %d, %d", timePassed, extra, extra + 1);
  radio.send(2, buff, strlen(buff));   //receiver node ID, message, length of message
  
  wait(550);
  if (!radio.receiveDone()) {};       //hang until data arrives
  if (radio.receiveDone()) Serial.println((char*) radio.DATA);
  wait(500);
}


Data flows from the bottom code to the top code, but nothing seems to occur when I send the "Got it".

syrinxtech

Quote from: jrdoner on January 24, 2016, 10:46:59 PM
I have set up a simple example using nodes 1 and 2, with 2 transmitting data to 1.  That works fine, but I cannot seem to get a transmit path working in the other direction.  Does this require promiscuous mode, or some  secret handshake?

The radios initialize just fine.  My loop()s for these two programs are shown below.

void loop() 
{
  int txRSSI;
 
  
  if (radio.receiveDone())
  {
      Serial.print("Tx node: "); Serial.println(radio.SENDERID);
      Serial.print("message "); Serial.println((char*)radio.DATA);
      txRSSI = radio.RSSI;
      Serial.print("RSSI "); Serial.println(txRSSI);
      Serial.println();  
 
  
    wait(500);
    radio.send(1,"Got it", 6);
  }  
}



void loop() 
{
  String stuff;
  int extra;
  
  timePassed = timePassed + 2;
  extra = timePassed + 3;
  
  sprintf(buff, "Init %d, %d, %d", timePassed, extra, extra + 1);
  radio.send(2, buff, strlen(buff));   //receiver node ID, message, length of message
  
  wait(550);
  if (!radio.receiveDone()) {};       //hang until data arrives
  if (radio.receiveDone()) Serial.println((char*) radio.DATA);
  wait(500);
}


Data flows from the bottom code to the top code, but nothing seems to occur when I send the "Got it".


I think your problem might be the double radio.receiveDone() calls in the second set of code.  The first call to radio.receiveDone() would get the "Got it" and clear the buffer.  When you call it again the message has already been received and processed so there is nothing let to receive.  Why the double call?

jrdoner

I only put the first one in thinking that the

if (!radio.receiveDone)){};

line would cause the code to wait for actual data to arrive.

I was getting no response before I put that line in, also.

syrinxtech

Seems to me it might be safer and more reliable to re-code this project using ACKs.  You could include the "Got it" message as part of the ACK.

The line:

if (!radio.receiveDone()) {};

isn't going to wait for anything.  The radio.receiveDone() call simply checks the incoming radio queue to see if anything has come in over the radio destined for this Moteino.  If yes, you process the data and if not, you move on to the next line of code.  The return is simply "true" or "false".  Since you threw in a NOT operator, the line of code translates to "If there is no data incoming on the radio, do nothing (empty braces)".  If you wanted this code to hold there until data came in you should probably change the "if" to a "while".  Of course, that is going to block the whole program until something does come in so from an efficiency standpoint that's probably not good programming.

I would look at the many examples from Felix and others on the Forum and re-write using ACK's.  Just my $0.02.

jrdoner

You are correct about the need for a "while" statement.   But its not just an ACK that I want.  My point is that if I establish data flow in one direction, I can't seem to get data flow (not just ACK;s) going in the other direction.  Every example I've checked involves one way data flow.

Felix

jrdorner,
A lot of my examples involve 2 way. Just look at the examples, for instance DoorBell, GarageMote, SwitchMote all receive
and transmit at various times.
The mating sketch that talks to them is the PiGateway sketch or MightyHat. They both listen to serial for a request and then forward the message to the listening node.
In fact I would start simply with the Gateway and Node sketches which also do that. Every 3rd received packet, the Gateway example will send a packet to the node and also request an ACK from the node.

jrdoner

Felix,.

I have looked at the doorbell example, and others.  Indeed, they involve two-way comm.   But I am trying to achieve two-way comm, basically doing the same thing, and I seem to get one way comm only.  I am using a Moteino and a  MoteMega.  None of the radio pins on either one are attached to anything external.  Both units can send and receive, because I can reverse their roles and get comm in the other direction, but never in both directions.  I will put my simplified loops here, just a few lines total, and please just tell me what is wrong with this code.  There must be something I don't get.

void loop()                //node 2 -- receive first, then transmit            
{
 
  if (radio.receiveDone())
  {
      Serial.print("Tx node: "); Serial.println(radio.SENDERID);
      Serial.print("message "); Serial.println((char*)radio.DATA);
      Serial.println();  
      
      wait(100);                  //0.1 sec. delay 
      data = data + 2;
      sprintf(buff, "Data %d", data);
      radio.send(1, buff, strlen(buff));
  }  
}


void loop()                      //node 1 -- transmit first, wait for reply
{
  
   data = data + 2;
  
   sprintf(buff, "Data =  %d", data);
   radio.send(2, buff, strlen(buff));             //receiver node ID, message, length of message
   wait(100);   
   if (radio.receiveDone())
   {
     Serial.print("Tx node: "); Serial.println(radio.SENDERID);
     Serial.print("message "); Serial.println((char*) radio.DATA);
   }  
   wait(800);     
}

Felix

BIG RED FLAGS: using delay()
Remove those completely, you want to hit receiveDone as often as possible. Look at how I implement delays, I never use delay(), I always just look at the "time" using millis and remember when I last did something with an unsigned long (uint32_t) variable. Look in the Node and Gateway examples for that.

jrdoner

Felix,

OK, I removed the wait() functions (differs from delay() in that I use timer 2 to operate wait()).
I did use millis() to stop it from running so fast that many transmissions were skipped.  Still the same results as before. 
Here's the two versions now.  Node 1 gets data to node 2, but not the other way.

void loop()                      //node 1 -- transmit first, wait for reply
{
  
   data = data + 2;
  
   sprintf(buff, "Data =  %d", data);
   radio.send(2, buff, strlen(buff));             //receiver node ID, message, length of message
   
   if (radio.receiveDone())
   {
     Serial.print("Tx node: "); Serial.println(radio.SENDERID);
     Serial.print("message "); Serial.println((char*) radio.DATA);
   }  
   while (millis() < clockTick) {};
   clockTick = millis() + 250;
       
}


void loop()                //node 2 -- receive first, then transmit            
{
 
  if (radio.receiveDone())
  {
      Serial.print("Tx node: "); Serial.println(radio.SENDERID);
      Serial.print("message "); Serial.println((char*)radio.DATA);
      Serial.println();  
                    
      data = data + 2;
      sprintf(buff, "Data %d", data);
      radio.send(1, buff, strlen(buff));
  }  
}





Felix

This is the same thing as delay():

while (millis() < clockTick) {};


You have to keep looping, and not stop and do nothing for 250ms. When you do while (bla) {}, that means your program spends 250 ms in {} (ie doing nothing). Does that make sense?

So what you're doing is:

receiveDone()
sleep 250ms
receiveDone()
sleep 250ms
receiveDone()
sleep 250ms
... forever

Again, look at my sketches, i never use any delays or empty while loops.

jrdoner

Felix, I finally got both units talking to each other, but I would like, if possible, to understand some of the details, re buffers and timing.

1. If an incoming buffer is not read before the next arriving transmission, what happens?  Is there an overflow flag somewhere?

2. The transceiver uses pin 2 as an interrupt.  Is it possible for me to somehow piggyback into that interrupt to get to incoming data as quickly as possible?

3. As you say, you never use and delays.  But if I know I have to wait for a single receipt that should be arriving, why can't I use a delay?

Felix

You get the data by calling receiveDone() and then the examples show how to read it.
You could write your own interrupt by why would you do that since the lib does it for you...
There is no overflow flag. You just have to be on the lookout for your packets. Since they can generally arrive any time you should be listening all the time and not sleeping the micro, or else your other node will send the packet while this one is sleeping and will time out waiting for an ACK, then you wonder why it doesn't work. That is why delay() is a no no.

jrdoner

I'm creating a network with 6 nodes, with a master node and 5 remotes.  The master talks to the remotes, and vice versa.  The remotes have different sensor arrays to read and report on, and the master may also send them data at any time.  Maybe I could write main loops that listen all the time for the received traffic, but it seems like a much more complicated way of doing it then looking for data on a regular schedule.

I have written a program which ping pongs data between two Motes,  It uses timer2 to look for traffic at 0.1 sec. intervals.  I'm not using ACKS.  Each radio gets a packet, and sends a reply 0.5 sec. later.  So I'm listening at five times the rate I'm sending.

This code will run just fine, for an arbitrary length of time.  Maybe for 5 cycles, maybe for 200, but eventually it always hangs.  Code is below.  The only difference between the programs is that one of them sends a first packet, during Setup().

#include <RFM69.h>
#include <SPI.h>

#define NETWORKID    100                 //all nodes in network must use same network ID
#define NODEID       1                   //this node is 1
#define FREQUENCY    RF69_433MHZ
#define ENCRYPTKEY  "adjjsk#6))L3ooa*"  //16 characters, and the same for all communicating nodes

RFM69 radio;

boolean msgArrived;
boolean checkReceive;
volatile unsigned long clockTick;
volatile unsigned long secTick;
int LEDPin = 9;


typedef struct
{
  int nodeID;
  int cmdType;
  int param1;
  int param2; 
  int param3;
  int param4;
}payload;

payload inData;

void setup( )
{
  radio.initialize(FREQUENCY,NODEID,NETWORKID);
  radio.encrypt(ENCRYPTKEY);
 
  //Serial.begin(115200);
  pinMode(LEDPin, OUTPUT);
  digitalWrite(LEDPin, LOW);
  
  //setup 2 kHz. timer for various timing operations
   
  cli();                                 //disable all interrupts
                                          //setting up timer2 for 0.5 ms. cycle
   TCCR2A = 0;                            //clear both timer2 configuration registers: gonna use as a 0.5 msec. timer
   TCCR2B = 0;  
   TCNT2  = 0;                            //set the counter register to 0, and set  
   OCR2A = 255;                           //128x64/16000000 = 0.512 ms.  
   TCCR2A = TCCR2A | B00000010;           //turn on CTC mode
   TCCR2B = B00001011;                    //CS1:2:0 to 101 to operate at clock speed/64 
                                          //this also establishes that WGM13:0 = 0100, i.e., CTC mode
   TIMSK2 = TIMSK2 | B00000010;           //enable the CTC interrupt                                                 
   sei();  
 
  msgArrived = false;
  checkReceive = false;
  clockTick = 0;
  secTick = 0;

  inData.nodeID = 1;
  inData.param1 = 0;
  radio.send(2, (const void*)(&inData), sizeof(inData));            
  Serial.println("Initial data sent");
  
}  //setup


ISR(TIMER2_COMPA_vect)           
{
  clockTick++;                    //counts half milliseconds
  if ((clockTick % 2000) == 0) secTick++;
  if ((clockTick % 200) == 0) checkReceive = true; 
}//ISR(Timer0..

void wait(int millisec)
{
  unsigned long later;
  
  later = 2*millisec + clockTick;
  while (clockTick < later) {};
}  


void loop()                      //node 1 -- transmit first, wait for reply
{
  if (checkReceive) checkTraffic();
  
  if (msgArrived)
  {  
     inData.param1 = inData.param1 + 1;
     wait(500);
     radio.send(2, (const void*)(&inData), sizeof(inData));  
     msgArrived = false;
     radio.receiveDone();
  }    
}//loop


void checkTraffic()
{  
   cli(); 
   if (radio.receiveDone())
   {
    // Serial.println();
   // Serial.print("Received from node "); Serial.println(radio.SENDERID);
     inData = *(payload*)radio.DATA;   
    // Serial.print("  received data: "); Serial.println(inData.param1);
      
     digitalWrite(LEDPin, HIGH);
     wait(100);
     digitalWrite(LEDPin, LOW);
     msgArrived = true;
    
   } 
   checkReceive = false; 
   sei();  
}



I really don't understand why this has to be so difficult;  I've used simple OOK 3 pin radios before, and  never had such difficulties.

1. Do I need some pullups somewhere?

2. Do I need to clear a buffer somewhere?

TomWS

@jrdoner, I would suggest drawing a state diagram to show the different states each node can be in and the events that are necessary to trigger each state change (including the conditions that KEEP a state from changing - like missing a packet to take a random example).

I'm not sure why you call receiveDone() at the bottom of loop() - if a message comes in and this call fields it, you've lost it because you don't check the result.

I'd also look at why you time when you call checkTraffic().  ISTM that you would use the timer to control when your node sends, but let the other node control the timing on when you receive (ie, when it sends).

Tom

jrdoner

TomWS,

You're right about the call to receiveDone() at the end of the loop.  It was an experiment on my part to see if it would somehow clear a buffer ,or something.  But the code behaves exactly the same with that instruction removed: it ping pongs successfully for some length of time and then hangs.

As to one node controlling another, why should I need to do that?  Consider the air traffic control system; the pilots don't tell the controllers when to talk, nor vice versa.  If these radios require that, they're of very limited utility. 

But the point is, why should this run for some period of time, then stop?  If its due to hardware, it usually is something like a missing pullup/down resistor.  If its software, then maybe its some sort of memory/variable overflow.  I have fiddled with this for a week of evenings, and I have used other radios that worked fine in similar contexts.   This seems like a perfectly reasonable way to check for traffic.