XBee Node Discovery problem

I am building a small network with about 10 XBee Series 2 transceivers and when I start up the network I want to do a node discovery to find all of the nodes on the network.

I developed the code shown below and it works find when there are only 3 nodes, coordinator and 2 routers. When I add additional nodes problems arise, the number of bytes read in is only 63. The packet length for a node discovery response is 29. The first two sets of 29 bytes of the 63 bytes are correct and correspond correctly to two of the routers on the net.

The code is rough right know so ignore the routines that get input from the user, this works fine and the node addresses are being parsed out correctly. I think the problem lies in the following statement:

n_bytes = Serial1.available();

Is there a buffer that is being overloaded when the 3rd or more router is added into the net?

Thanks for looking,

wade

#define max_Nodes 40
#define net_delay_time 10000 // How long to wait for modules top check in
int Verbose = 0;

// Function Prototypes
void node_discovery(void);
void send_packet(byte, int);
int get_node_number(void);
int get_UI(int);
int get_node_location(void);

int  n_sensors;
int i;
int add_start = 14;


byte Node_16_add_MSB[max_Nodes], Node_16_add_LSB[max_Nodes];
int node_location[max_Nodes], node_number[max_Nodes];

byte value;

void setup()
{
  Serial.begin(9600);  //  For serial monitor 
   Serial1.begin(9600); // For XBee
   Serial.println("Initializing System"); 
   node_discovery();
}

void loop()
{
  while(1) {}
}


void node_discovery()
/*  
From Faludi page 291:
ND is an AT command that discovers and reports all RF modules
found.
*/
{
  int n_bytes;
  byte discard;
  int i_offset = 0;
  int curr_cnt;
  unsigned long bit_address, crap1, crap2, crap3, crap4;
 
 /*
  The packet below is from an example in the XBee Pro manual page
  57.
*/
byte ND_packet[] = 
    {0x7E, 0x00, 0x04, 0x08, 0x01, 0x4E,0x44, 0x64};

    Serial.println("Inside node_discovery()");


send_packet(ND_packet, sizeof(ND_packet));

  delay(net_delay_time);  // Delay giving nodes time to respond
  
  n_bytes = Serial1.available();    // IS THE BUFFER BEING OVER LOADED?
  
  if(n_bytes  > 1)
  {
    discard = Serial1.read(); 

    if(discard == 0x7e)
      n_sensors = 1;
      {  
        for (i = 1; i < n_bytes; i++) {
          discard = Serial1.read();
           if(discard == 0x7E)
          { 
            n_sensors++;
            i_offset =+ 29;
         }
          curr_cnt = i - i_offset;

//  Get lower 32 bits of current ZigBee address
        if(curr_cnt >= add_start & curr_cnt < add_start + 4)
        {
        if(curr_cnt == add_start)
          {
            crap1 = (unsigned long)discard << 0x18; 
          }
          else if(curr_cnt == add_start + 1)
           {          
            crap2 = (unsigned long)discard << 0x10;     
           }
          else if(curr_cnt == add_start + 2)
           {
            crap3 = (unsigned long)discard << 0x08;
          }
          else  // no if as if you are here curr_cnt == add_start + 3
           {
            crap4 = (unsigned long)discard;
 //            Serial.print("Crap4 = "); Serial.println(discard, HEX);
            bit_address = crap4 | crap3 | crap2 | crap1;  

            // Output results
 
            Serial.print("Node with the 32-bit address ");
            Serial.print(bit_address, HEX);
            Serial.println(" checked in");
       
          }
        }
        if(curr_cnt > 7 & curr_cnt < 10)
        {
          
          if(curr_cnt == 8) // MSB of 16-bit address
          {
             Node_16_add_MSB[n_sensors] = discard;
          }
          else   // LSB of 16-bit address
          {
             Node_16_add_LSB[n_sensors] = discard;
 
          }
          }
          
          }


      }
  }
  
 
void send_packet(byte packet[], int n_bytes)
/* 
This Routine just sends a packet out from the XBeePro Module, to send from 
another radio system just change the function from XBeeSerial() to the new 
radio serial port function and all is good.
*/
{
for (int ik = 0;ik < n_bytes;ik++)
  {
  Serial1.write(packet[ik]);

  }
}

The serial interface is quite slow, so you are not likely to see a complete message buffered in it each time you check, and if the message is too large you will lose part of it if the serial buffer fills up.

A better approach is to read data from the serial port into a local buffer as it becomes available piece by piece, then act on that buffer once it has stored a full message.

In addition to dhunt's comments, the serial buffer size in 1.0 is 64 bytes. That you are getting packets around this size as a limit implies that you are not reading often enough.

    discard = Serial1.read(); 

    if(discard == 0x7e)

Great name for a variable that is used to hold an important value. Not!

            crap1 = (unsigned long)discard << 0x18;

Another great name for important data. Not!

          }
          }
          
          }


      }
  }

Nice consistent indenting and use of white space. At the risk or repeating myself, not!

As I stated in my original post the code is rough and maybe my choice of variable names doesn't please all but to me they are self documenting.

Anyway, I was waiting too long to read in from the buffer. I read all of the data into an array and then parsed it out and that worked. Here is the code snippet that I used to read in from the network nodes

  start_t = millis();
  
  while(millis() - start_t < 10 * net_delay_time)
  {
    n_bytes = Serial1.available();
    if(n_bytes > 0)
    {
      for(int ij = 0; ij < n_bytes; ij++)
      {
        read_buf[i_count] = Serial1.read(); 
        Serial.print("n_bytes = ");Serial.print(n_bytes);
        Serial.print("i_count = ");Serial.print(i_count); 
        Serial.print(" byte = ");Serial.println(read_buf[i_count], HEX); 
        i_count++;
      }
    }
    delay(net_delay_time / 10);
  }

Thanks for the help,

wade

  while(millis() - start_t < 10 * net_delay_time)
  {
    // Read all available data, even though it may not be a complete packet
    delay(net_delay_time / 10);
  }

What is this while loop/delay about?

Are you not concerned about reading complete packets?

I guess you could wait until there are enough bytes buffered in the serial buffer, but as PaulS pointed out the buffer is only 64 bytes in size and you run into danger of losing bytes if the buffer fills.

If I was doing this I would read in the header (which is only 3 bytes), get the length of the message from the header, then read in the body of the message. No need to hold your sketch up doing delays. For example (compiles but not tested):

#include "zb.h"

zb_message_t msg;

void setup()
{
    Serial.begin(9600);
    Serial1.begin(9600);
}

void loop()
{
    if (zigread(&msg) > 0) {
        Serial.println("Got a message");
    }
}

/* read in a zigbee message as it becomes available.
 * When the full message is ready, return the size of the message.
 * If the full message is not ready, return 0.
 */
int zigread(zb_message_t *msg)
{
    int data;
    static boolean waitingForHeader = true;
    static boolean gotFrameStart = false;
    static uint8_t bufInd = 0;
    static uint8_t checksum = 0;

    if (!gotFrameStart) {
       if (Serial1.available() && (Serial1.read() == ZB_FRAME_START)) {
          gotFrameStart = true;
       } else {
          /* Still waiting for start of frame */
          return 0;
       }
    }

    if (waitingForHeader) {
	if (Serial1.available() < sizeof(zb_header_t)) {
	    /* still waiting */
	    return 0;
	} else {
            /* read in the header */
	    for (int ind=0; ind<sizeof(zb_header_t); ind++) {
		((uint8_t *)msg)[ind] = Serial1.read();
	    }
	    /* fix up size */
	    msg->header.length = ntohs(msg->header.length);

	    /* time to start reading the body */
	    waitingForHeader = false;
	    bufInd = 0;
	    checksum = msg->header.apiId;
	}
    } else {
	while (Serial1.available()) {
	    data = Serial1.read();
	    msg->payload[bufInd++] = (uint8_t)data;
	    checksum += data;
	    if (bufInd >= msg->header.length) {
		/* Got the full message */
		waitingForHeader = true;
		gotFrameStart = false;

		/* See if the checksum is correct */
		checksum = 0xFF - checksum;
		if (checksum == 0) {
		    /* Good message, return size */
		    return bufInd + sizeof(zb_header_t);
		} else {
		    /* Bad checksum, discard the message */
		    Serial.println("Bad checksum");
		    return 0;
		}
	    }
	}
    }

    return 0;	/* waiting for more data */
}

And zb.h is this:

#ifndef _ZB_H_
#define _ZB_H_

#include <Arduino.h>

/* convert network order uint16_t to host order */
#define ntohs(value)	(((uint16_t)(value)) << 8 | (((uint16_t)(value)) >> 8))

#define ZB_MAX_PACKET_SIZE		128
#define ZB_FRAME_START			0x7e

typedef struct {
    uint16_t	length;	/* Length of payload including checksum */
    uint8_t	apiId;
} zb_header_t;

typedef struct {
    zb_header_t header;
    uint8_t payload[ZB_MAX_PACKET_SIZE - sizeof(zb_header_t)];
} zb_message_t;

#endif

Okay maybe that was a bit ambitious. Here's a simple version (compiles, not tested):

void setup()
{
    Serial.begin(9600);
    Serial1.begin(9600);
}

uint8_t msg[128];
uint8_t msgInd = 0;	/* Current index into msg buffer */
uint8_t msgApi = 0;	/* API of last received message */
int msgSize = -1;	/* Size of message to receive */
boolean waitingNewMessage = true;

void loop()
{

    if (Serial1.available() > 0) {
        if (waitingNewMessage) {
	    if (Serial1.read() == 0x7E) {
	    	/* Got start of frame */
		waitingNewMessage = false;
		msgInd = 0;
		msgSize = -1;
	    }
	} else {
	    if (msgSize < 0) {
	    	/* waiting to read message size and API */
		if (Serial1.available() >= 3) {
		    /* read in the two bytes for the message size */
		    msgSize = Serial1.read() | ((int)Serial1.read()) << 16;

		    /* Read in the message API */
		    msgApi = Serial1.read();
		}
	    } else if (Serial1.available()) {
	       /* Got another byte for the message buffer */

	       /* Don't put too much data in the buffer */
	       if (msgInd < sizeof(msg)) {
		   msg[msgInd] = Serial1.read();
	       }
	       msgInd++;
	    }
	}
    }

    if (msgSize == msgInd) {
    	/* Got a message */
	waitingNewMessage = true;	/* ready to start receiving the next message */
	msgInd = 0;

	/* deal with the message */
	Serial.print("Got a message, ");
        Serial.print(msgSize);
        Serial.print(" bytes long");
    }
}

Thanks for the helpful hints. I had some problems with the first code dhunt posted and came up with code similar to his second posted code.

After doing extensive programming in FORTRAN for the last 30 years doing numerical analysis and only dabbling in C when needed I am not fully comfortable with pointers and structures yet.

Thanks

wade

Hi Wade,
Please could you post your final sketch?