Tuesday, October 30, 2012

Using the XBee library

Recently Paul Stoffregen created a new software serial library for the Arduino, then he modified Andrew Rapp's XBee library to use it (as well as the older, but really great, SoftwareSerial libray).  Suddenly, the XBee library is something that could be really useful.

See, the XBee library has traditionally been hooked into the hardware serial lines on Arduino Pins 0 and 1.  That means getting debugging information out of the device combination of an Arduino and an XBee is an exercise in frustration and annoyance.  Sure, you can use blinking lights to tell you what is going on, but this is the 21st century.  I have always written my own code to handle the XBee interaction because I wanted the serial port to provide status and control when I was working on a project.  Additionally, Andrew's library is heavily c++ and all the classes, inheritance, and instances confused the heck out of me.

However, maybe with the inclusion of a modified software serial port, and the capability to use the additional ports on an Arduino Mega, now is the time to take a serious look at using it.  I mean, if you have a mega, you can now put the XBee on Serial1 and keep Serial and Serial 2 for other things; this is just the ticket for converting RS485 into XBee packets to send over the air where you don't have wires.  Heck, I can think of dozens of things.

So, here is my first voyage into the use of the library.  I haven't linked in Paul's new serial library yet, I will on a future example, but I just wanted to understand what was going on and how to, at least, receive a packet and do something with it.  I took one of Andrew's library examples and modified it substantially to include many of the things I need in using an XBee and commented the heck out of it so others stood a chance of understanding what was going on.  It's amazing how few examples are out there that actually show what you need to do to do something along these lines.


The Arduino Sketch

/**
I took Andrew Rapp's receive example and modified it to be completely
unrecognizable.

I wanted to experiment with and better understand how to use his XBee
library for an actual project.  With the inclusion of support for SoftwareSerial
so that the XBee can be put on digital pins leaving the Arduino serial port
available for debugging and status, the library's usefullness shot way up.

This is a HEAVILY commented example of how to grab a receive packet off the
air and do something with it using series 2 XBees.  Series 1 XBees are left as
and exercise for the student.
*/

#include <XBee.h>
#include <SoftwareSerial.h>

XBee xbee = XBee();
XBeeResponse response = XBeeResponse();
// create reusable response objects for responses we expect to handle
ZBRxResponse rx = ZBRxResponse();

// Define NewSoftSerial TX/RX pins
// Connect Arduino pin 2 to Tx and 3 to Rx of the XBee
// I know this sounds backwards, but remember that output
// from the Arduino is input to the Xbee
#define ssRX 2
#define ssTX 3
SoftwareSerial nss(ssRX, ssTX);


void setup() {
  // start serial
  Serial.begin(9600);
  // and the software serial port
  nss.begin(9600);
  // now that they are started, hook the XBee into
  // Software Serial
  xbee.setSerial(nss);
  // I think this is the only line actually left over
  // from Andrew's original example
  Serial.println("starting up yo!");
}

void loop() {
    // doing the read without a timer makes it non-blocking, so
    // you can do other stuff in loop() as well.
    xbee.readPacket();
    // so the read above will set the available up to
    // work when you check it.
    if (xbee.getResponse().isAvailable()) {
      // got something
      Serial.println();
      Serial.print("Frame Type is ");
      // Andrew call the frame type ApiId, it's the first byte
      // of the frame specific data in the packet.
      Serial.println(xbee.getResponse().getApiId(), HEX);
   
      if (xbee.getResponse().getApiId() == ZB_RX_RESPONSE) {
        // got a zb rx packet, the kind this code is looking for
     
        // now that you know it's a receive packet
        // fill in the values
        xbee.getResponse().getZBRxResponse(rx);
     
        // this is how you get the 64 bit address out of
        // the incoming packet so you know which device
        // it came from
        Serial.print("Got an rx packet from: ");
        XBeeAddress64 senderLongAddress = rx.getRemoteAddress64();
        print32Bits(senderLongAddress.getMsb());
        Serial.print(" ");
        print32Bits(senderLongAddress.getLsb());
     
        // this is how to get the sender's
        // 16 bit address and show it
        uint16_t senderShortAddress = rx.getRemoteAddress16();
        Serial.print(" (");
        print16Bits(senderShortAddress);
        Serial.println(")");
     
        // The option byte is a bit field
        if (rx.getOption() & ZB_PACKET_ACKNOWLEDGED)
            // the sender got an ACK
          Serial.println("packet acknowledged");
        if (rx.getOption() & ZB_BROADCAST_PACKET)
          // This was a broadcast packet
          Serial.println("broadcast Packet");
       
        Serial.print("checksum is ");
        Serial.println(rx.getChecksum(), HEX);
     
        // this is the packet length
        Serial.print("packet length is ");
        Serial.print(rx.getPacketLength(), DEC);
     
        // this is the payload length, probably
        // what you actually want to use
        Serial.print(", data payload length is ");
        Serial.println(rx.getDataLength(),DEC);
     
        // this is the actual data you sent
        Serial.println("Received Data: ");
        for (int i = 0; i < rx.getDataLength(); i++) {
          print8Bits(rx.getData()[i]);
          Serial.print(' ');
        }
     
        // and an ascii representation for those of us
        // that send text through the XBee
        Serial.println();
        for (int i= 0; i < rx.getDataLength(); i++){
          Serial.write(' ');
          if (iscntrl(rx.getData()[i]))
            Serial.write(' ');
          else
            Serial.write(rx.getData()[i]);
          Serial.write(' ');
        }
        Serial.println();
     
        // So, for example, you could do something like this:
        handleXbeeRxMessage(rx.getData(), rx.getDataLength());
/*
        // I commented out the printing of the entire frame, but
        // left the code in place in case you want to see it for
        // debugging or something
        Serial.println("frame data:");
        for (int i = 0; i < xbee.getResponse().getFrameDataLength(); i++) {
          print8Bits(xbee.getResponse().getFrameData()[i]);
          Serial.print(' ');
        }
        Serial.println();
        for (int i= 0; i < xbee.getResponse().getFrameDataLength(); i++){
          Serial.write(' ');
          if (iscntrl(xbee.getResponse().getFrameData()[i]))
            Serial.write(' ');
          else
            Serial.write(xbee.getResponse().getFrameData()[i]);
          Serial.write(' ');
        }
        Serial.println();
*/
      }
    }
    else if (xbee.getResponse().isError()) {
      // some kind of error happened, I put the stars in so
      // it could easily be found
      Serial.print("************************************* error code:");
      Serial.println(xbee.getResponse().getErrorCode(),DEC);
    }
    else {
      // I hate else statements that don't have some kind
      // ending.  This is where you handle other things
    }
}

void handleXbeeRxMessage(uint8_t *data, uint8_t length){
  // this is just a stub to show how to get the data,
  // and is where you put your code to do something with
  // it.
  for (int i = 0; i < length; i++){
//    Serial.print(data[i]);
  }
//  Serial.println();
}

// these routines are just to print the data with
// leading zeros and allow formatting such that it
// will be easy to read.
void print32Bits(uint32_t dw){
  print16Bits(dw >> 16);
  print16Bits(dw & 0xFFFF);
}

void print16Bits(uint16_t w){
  print8Bits(w >> 8);
  print8Bits(w & 0x00FF);
}

void print8Bits(byte c){
  uint8_t nibble = (c >> 4);
  if (nibble <= 9)
    Serial.write(nibble + 0x30);
  else
    Serial.write(nibble + 0x37);
     
  nibble = (uint8_t) (c & 0x0F);
  if (nibble <= 9)
    Serial.write(nibble + 0x30);
  else
    Serial.write(nibble + 0x37);
}

I tried to get all the major items out of the packet so they could be displayed and even included a tiny routine to illustrate using the received data.  This is what it looks like running:


You have to use the scrollbar at the bottom to see all of the message, but I wanted it that way instead of worrying about wrapping around.  I also put in print routines for the hex data; I love being able to see it in both hex and ascii.

Grab it, modify it to your purpose, and have fun.

48 comments:

  1. Hello,
    I was delighted to find your example as there don't seem to be many people using XBees on a software serial port.

    I have a lilypad main board connected to an XBee breakout board using conductive thread.
    I am using digital pins 2 and 3 on the Lilypad just like you as I'm keeping the hardware serial pins 0 and 1 free
    for use by the FTDI which I use for either a USB connection to my PC or a Bluetooth wireless connection to my laptop
    - I have a bluetooth modem with a FTDI header that I can connect to the lilypad for this. Output from Serial.print() etc.
    is then either displayed in a serial terminal window on the PC or laptop as expected.

    The laptop is running a Processing sketch that is broadcasting all the time, although I really only want this sender
    to broadcast every minute. I have a series 2 XBee radio (with coordinator firmware) connected to the laptop using an
    Explorer module. Here is an example of one of the transmit requests being broadcast:

    0x7e,0x00,0x1b,0x10,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xfe,0x00,0x08,0x45,0x41,0x01,0x00,0x50,0x52,0x49,0x4d,0x41,0x52,0x59,0x47,0x57,0x9b

    The only thing in this frame that I am unsure about is the 0x08 Options. the RF data and address fields look good.

    The red Tx led is on nearly all the time on the sender's Explorer as expected.
    On the lilypad's XBee breakout board, the power led is on and hte green rssi led only comes on occasionally.

    My big problem is that NONE of the XBee messages are getting through to the Lilypad!
    The lilypad is running your sketch from http://www.desert-home.com/2012/10/using-xbee-library.html
    I just put in an extra Serial.print to the else clause that you hate and of course it is being executed
    which indicated to me that the xbee.readPacket() is not working. No data is being received and there are
    no errors! Just as if the sender wasn't running at all....

    If you have any ideas about what might be wrong please let me know.

    Thanks in advance,
    Jackie.

    ReplyDelete
  2. First check two things for me, First make sure that both of the XBees are set for API mode and that they are using API mode 2. I've had a number of people have trouble and seek advice when that was their problem. Next, put a delay in the processing code to only transmit every couple of seconds. Using SoftwareSerial can chew up a lot of processing power on the little arduino and things get backed up and dropped on the floor.

    I had this happen big time on a project where things were coming in so fast that I couldn't process them on the board. Slowing down the conversation a tiny bit made it work perfectly every single time. I was getting behind and missing things like the checksum, start sentinel, etc and never managing to construct a complete packet.

    Try these two things and let me know

    ReplyDelete
  3. Thank you very much for getting back to me so quickly.

    I was very careful configuring each radio in X-CTU but I double-checked again and AP=2 on each radio.
    They each have the same PAN ID = 12345
    I also did a "Network Discovery" from the MoltoSenso Network Manager on the same laptop as the XBee coordinator.
    The other radio on the Lilypad was successfully discovered so it looks like the network is set up ok.

    I changed the delay between broadcasts from the sender (on the laptop).
    I tried a few different intervals like 0 (continuously broadcasting), 10, 30 and 60 seconds.
    No errors are ever reported by the sender but the XBee on the Lilypad never gets a message!

    Here is an example of a Transmit Status that the sender receives after a broadcast:

    [com.rapplogic.xbee.api.InputStreamThread] Received packet: int[] packet = {0x00, 0x07, 0x8b, 0x31, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x46};

    The delivery status and discovery status are both 0x00 for success.
    The 16-bit destination address is 0xFFFE which I assume is OK as it matches what I had in the Transmit Request that was broadcast.

    I don't know what to change on the Lilypad to get it to receive a message....

    ReplyDelete
  4. I agree with you about the options byte that is being sent by the processing code. According to the XBee user's guide, that's an undefined value and I don't have a clue if it matters or not. However, the transmit response indicates it found an XBee and actually delivered the message to it.

    So, everything indicates you sent something and it should have popped out the serial port of the XBee to the LilyPad you're using. Try checking the obvious: did you get the pins backwards such that output is hooked to output? Try switching them while it's running; that doesn't hurt anything. Did one or both of the wires from the XBee to the serial input break? You can also reverse the two XBees so that the coordinator is on the lilypad and the router is on the pc to see if something strange is going on.

    You can get the code to transmit I have on this blog and send from the lilypad to the pc; you'll have to modify it a tiny bit to use softwareserial, I think my example used a mega2560 with multiple serial ports. By transmitting you may turn up a clue what's going on.

    You can maybe try using different digital ports to see if there is a possible problem with either pin 2 or 3. Maybe temporarily hook the XBee to pins 0 and 1 so you have the leds on the board to tell you if data is coming in. If you have an arduino around, try plugging the XBee into it and running the same code to see if there's a difference.

    In a fit of desperation, look at the processing library to see if changing that darn 80 into 0 is possible; although I don't think it's a problem, I haven't seen that before.

    Let me know how it goes.

    ReplyDelete
    Replies
    1. Oh, one other thing. The else statement you mentioned doesn't indicate that the read failed, it just indicates that there is not an available packet nor was there an error. If you take out all the chaff I put in there, it will look like this:

      void loop() {
      xbee.readPacket();
      if (xbee.getResponse().isAvailable()) {
      }
      else if (xbee.getResponse().isError()) {
      }
      else {
      // I hate else statements that don't have some kind
      // ending. This is where you handle other things
      }
      }

      I intentionally did that to allow the little computer to be doing something besides just hanging looking for a packet from the XBee. So, what you're seeing with a debug statement in the else is perfectly normal.

      I should probably get that out of there to keep from confusing folk.

      Delete
    2. Ok, fine, I just can't seem to get it together today. When I looked on the web for clues to this a lot of people have problems doing what you're doing. Most of them seem to have problems getting enough power to run both the lilypad and the XBee. In this case, the XBee will be the big power user and when it transmits, it could starve your power supply. I haven't worked with a lilypad, but it was designed to work off a small battery and the XBee needs more than that to transmit.

      You probably already know all this, if so, just ignore me.

      Delete
  5. Hi Dave,

    Thanks for all your suggestions. I am worried about the power supply for the lilypad alright as I only have a 3.7v lipo:
    https://www.sparkfun.com/products/341
    I try to keep it fully charged and the xbee's power led is on. I also run the lilypad when it is getting extra power from my PC via USB.
    The tx and rx connections are sewn with conductive thread so it is not easy for me to switch these around while running a sketch.
    I wish that Options byte wasn't 0x08 but nevertheless I am fairly confident that the msg from the laptop is reaching the lilypad's xbee breakout board as its RSSI led comes on when the sender's TX is on.
    Although, having said that, the RSSI led is never very bright.

    However I did modify that dreaded else clause on the lilypad to send a message to the coordinator on the laptop and those msgs are working!
    So I have one-way communication out of the lilypad but not in...

    I do have an Uno as well so I will try to do some further testing with that although it was exhibiting similar behaviour as the lilypad when I last checked.

    Thks v much for your help,
    Jackie

    ReplyDelete
  6. Jackie, having one-way comm eliminates a lot of stuff. Could the conductive thread from the XBee to the Lilypad have broken? It appears power, setup and every thing else is ok or it wouldn't transmit. A simple broken connection could be your problem.. Time for an ohm meter check between the boards. If that works, then there may be a hardware problem with the XBee, its board or the serial input pin you're using. Since you're using broadcast and not addresses, you can reverse the two XBees and see if the problem moves with the XBee, which would likely mean a bad XBee.

    You're getting closer, and some testing on the UNO should narrow it down a lot.

    ReplyDelete
  7. Hello Dave,

    I appreciate too much your xbee examples!!! It helped me a lot to make my Xbee API network. I was having problems to get the RSSI data. I am using the Xbee series 2 and have tried use rx.getRssi() but it didnt work, then I thats happens because I am using wrong function for this series. Andrew suggested use rx16.getRssi() on his site, but i think this function is for series 1. Would you help me on this mattter?

    Tks too much for your help,

    Alipio Carvalho

    ReplyDelete
  8. Alipio,

    As I understand it, the only way to get the rssi value from a series two XBee is to use an AT command which is frame type 8 and it will return an AT command response, frame type 88 that holds the register contents. The relevant API frames are on pages 103 and 110 of the User's guide.

    The command to send in the AT command is "DB" and is described on page 132. So you should be able use the XBee library's AT command interface and get the value back.

    The XBee document is at: http://ftp1.digi.com/support/documentation/90000976_G.pdf

    In theory, there is a way to get it using the digital pins I/O interface, but I've never heard of anyone successfully using it.

    You can even set an XBee up in AT mode, hook it to a PC, and use XCTU to test this before you try and program the frame construction on an Arduino.

    ReplyDelete
  9. Hello Dave,

    Thank you very much for your suggestion!!

    Alipio Carvalho

    ReplyDelete
  10. Hi Dave,

    My problem is fixed! My supervisor suggested using the AltSoftSerial library instead of SoftwareSerial. This involved switching to pins 8 and 9 on the lilypad. It has done the trick! I finally have 2-way ZigBee communication. I should have paid more attention to this recommendation on http://code.google.com/p/xbee-arduino/wiki/SoftwareSerialReleaseNotes

    Thank you very much for your help and great suggestions. Best of luck with your projects,
    Jackie

    ReplyDelete
  11. Jackie,

    That's great, but I wonder why you need to use the alt. Your description didn't indicate that you were running anything that would interfere with the software serial that comes with 1.0.1 or above.

    But, then again, it works! That's the important part.

    ReplyDelete
  12. Thanks to everyone for all this great information!

    Jackie -- perhaps the reason that switching pins worked has to do with the particular way in which softserial is setup on the Lilypad. The latest lilypad uses the same processor as the Leonardo does, and only particular pins on the Leonardo (including 8 and 9) are capable of software serial, I believe. This fellow provides a bunch of relevant detail on the issue in a blog post here:

    http://vort.org/2012/05/25/trouble-softserial-arduino-leonardo/

    I'll be using all this great info you've all collected here soon myself. Thanks again so much for posting.

    ReplyDelete
  13. Hello,
    I am working on designing an XBEE pro to form a mesh networking for image transmission. Could any body explain how i can achieve this? first i want to know if it is possible? I have 9 arduino uno board and 10xbee pro and a USB dongle connected to the computer. Can any one help with the codes, i am ready to pay. Thanks....................mail: ay4real@hotmail.ca

    ReplyDelete
    Replies
    1. Not sure what you mean by image transmission, but it's very likely that the XBees won't do it for you. They're designed for real time transmission of small packets over a mesh network to monitor and control devices. That doesn't include the large amounts of data that images require. You could easily flood an XBee network with a single image for several minutes. Yes, minutes.

      Images need a high rate of data transfer and you just can't get that from an XBee. Start looking at wireless ethernet techniques.

      Delete
  14. Hi Dave,

    Your site has made my efforts sooo much easier. Many Thanks. But I could use a little help.

    I'm trying to send data between two Arduino Uno's. From your examples, I've been able to make Arduino A talk to Arduino B with your "Hello world" and "I saw...". Yippee!

    I've looked around your site but have not seen where you send float, long and int type data. In my case I'm sending a variable - micros() of Arduino A to Arduino B so that the two are kinda time synced. Arduino B then does some calculations with external sensor inputs. Arduino B then sends back a variable (probably an unsigned long) to arduino A.

    Could you point me in the right direction as to how I would substitute your "Hello World" with these types of variables?

    Thanks

    Bill

    ReplyDelete
  15. There's a huge discussion of this on one of the pages ... somewhere in the comments. Basically what I do is to convert it to text then send it. When I get it at the other end, I convert it back to whatever I need. For integers, I use the c routine itoa() to get an ascii numeric representation and then atoi() to turn it back into an integer. Then, I found out that printf() actually worked on the arduino and I mostly converted everything to use that. A simple statement like:

    sprintf(Dbuf,"Power,%d.%02d,%d.%02d,%d.%02d,%d.%02d,%d.%02d,%d.%02d\r",
    (int)realPower, frac(realPower),
    (int)apparentPower, frac(apparentPower),
    (int)powerFactor, frac(powerFactor),
    (int)Vrms, frac(Vrms),
    (int)Irms, frac(Irms),
    (int)freq, frac(freq));

    should be a reasonable illustration of what can be done. the things like (int)variable, frac(variable) are taking the integer and fractional part of a float apart and converting it to ascii in the %d.%2d parts of the format string sent to printf. There are many ways of doing this, but I chose this one because it was easy. To get it back, you do something like:

    realPower = atof(strtok(0, ","));
    apparentPower = atof(strtok(0, ","));
    powerFactor = atof(strtok(0, ","));
    rmsVoltage = atof(strtok(0, ","));
    rmsCurrent = atof(strtok(0, ","));
    frequency = atof(strtok(0, ","));

    The sttrtok() call is used to separate the values and then atof() is used to turn them back into floats. I did it this way so I could monitor the traffic and actually see the values. Instead of trying to figure out what the values were from binary data, it's clearly readable. It also helps in debugging the code while you're working on it because you can print the string to the terminal and take a look at it, then at the other end look at it as it comes out of the XBee and see that it got there ok.

    Hope this helps.

    ReplyDelete
    Replies
    1. Thank you... I will try that tomorrow. I'm calling it a night.

      I just finished fiddling with code that broke a long into separate "parts" then reassembling them on the receiver end. I got it to sorta work. But since I'm a real beginner, my methodology is to try something then see what happens.

      But no doubt, your website has been of tremendous help. There is NO WAY I would have gotten this far with just the Xbee library docs, etc.

      I'll let you know

      Thanks again

      Bill

      Delete
    2. Floats are the problem since the arduino library doesn't handle them as well as other datatypes. It's a little confusing, but you'll get it in a couple of tries.

      And don't worry about trial and error programming. That's the way it's done these days.

      Delete
  16. Good evening Dave,

    I've been messing around with different types of variables and arrays all day. It seems I could have saved a great deal of time just listening to your comments about arrays.... way to slow...

    So thanks to your help, I've been able to convert a long to a string and successfully send the string. But I'm stuck on the receiving end. Your examples can println the string, but I don't know how to make it into a variable. Do you have an example somewhere of how to reconstruct a string variable on the receiving end. Once I get that string variable, I can convert it to a long.

    Thanks for all your help,

    Bill

    ReplyDelete
    Replies
    1. Bill, I decided to put together a blog post on this so I could include the code easily. I've gotten many questions on how to send this kind of data over an XBee or a serial connection of some sort, and now seems like a good time to address it in a way that people can cut and paste from. So, hit the button up top for the main page and take a look. Here's URL if that doesn't work http://www.desert-home.com/2013/10/floats-and-strings-over-serial-link.html

      Delete
    2. Good afternoon Dave,

      I still seem to be stuck on the receiver end, particularly around how to get the string from the rx.getdata() array.

      This is what I use to send my string... i.e. real simple

      unsigned long basetime = micros();
      ltoa(basetime, buf, 10);
      ZBTxRequest zbtx = ZBTxRequest(Broadcast, (uint8_t *)buf, strlen(buf));
      xbee.send(zbtx);

      On the receiving end, I can print all the data along with your other cool information print outs... but my basetime variable (as a string) seems to be only accessible by using the following -

      for (int i= 0; i < rx.getDataLength(); i++){
      //Serial.write(' ');
      if (iscntrl(rx.getData()[i]))
      Serial.write(' ');
      else
      Serial.write(rx.getData()[i]);
      Serial.write(' ');

      Is this "for" loop the only way to get the data. I studied your new blog code (thank you!) but ...well... am I missing something right in front of me?

      Thanks

      Bill

      Delete
  17. Nope, you're not missing anything that I can think of. Notice up there in my example how I print the text from the payload in a loop? That's because the data isn't guaranteed to have a null at the end of it to make it into a 'c' string.

    Let me 'splain what the loop you're using is actually doing. It's taking the characters out of the packet that was returned one at a time, checking to make sure the character isn't control character because that might mess up the display and the printing it using 'write' so there isn't any formatting done by Serial.print(). Since we don't know how long the buffer is and if it is safe to just stuff a null at the end of it, we have to copy it out into another place and stick the null at the end ourselves.

    so make a buffer to hold it and copy it character by character into the buffer and stick a null at the end. Then it's a real string and you can Serial.println( thenewbuffer); just fine. Actually, the code is almost there:

    char buffer[50];

    for (int 1=0; i<rx.getDataLength(); i++){
    buffer[i] = rx.getData()[i];
    }
    buffer[i] = 0;
    Serial.println(buffer);

    Taa Daa, buffer will now hold a string (if I didn't mess something up above). You can now use the new string any way a string can normally be used.

    Did this help?

    ReplyDelete
  18. Hi Dave,

    Yes that made all the difference. I did make that i=0; instead 1=0 and I took out the buffer[i]=0 ... but it seems to be ok. Now I'm working to reduce latency... more fun :-)

    Thanks again for all your help.

    Bill
    Thanks

    ReplyDelete
    Replies
    1. oops ...

      Be careful about not using the last buffer[i]=0; What can happen is that you put something longer in the buffer like "Hello World" then later put a number in it, you'll get something like "1.23o World" in the buffer which can cause you trouble. That's why most programmers stuff a null explicitly at the end of the text they're copying to be darn sure there's a null to terminate the string.

      Delete
  19. Hi Dave,

    Thanks for the advise. I've been redesigning the whole system and found a great way to simplify ... The biggest headache was getting the data transmitted with the correct time attached... time being accurate to a millisecond. I recently discovered the PPS signal from an Adafruits GPS. So now my arduinos are all synced and I can transmit much simpler data.

    ReplyDelete
    Replies
    1. I spent a couple of months messing around with time trying to get it right. Although I only want (not need) accuracy to the second, I couldn't get it. Finally I got one of Adafruit's GPS chips, combined it with an arduino and an XBee and put it in the attic. Now I have a time standard for the house that everything uses. They each sync their clock code to the house clock on boot and every time a new clock packet comes over the XBee network.

      I even have a tab for the clock up there somewhere. That was one of my best ideas.

      Delete
  20. I have a question. Will this code work on a Xbee Series 2 coordinator in API with 2 Xbee Series 2 routers in AT mode?

    ReplyDelete
    Replies
    1. Yes, but you have to be aware of what you'll get out. The routers, being in AT mode will only output the payload data to the serial port. Going the other way, what ever you send will come out the serial port in an API packet and have to be taken apart by the library.

      Delete
    2. Thanks for the reply! I got the code working & it works greats! But This still leaves me with a few other problems:

      In my network, there are 2 routers & 1 coordinator
      1. Error code 3 & Error code 1 comes up, & I think it may be due to my routers being in AT mode. My theory is they are both trying to talk to my coordinator at the same time & this creates a deadlock.
      2. Even when one of my routers are disconnected, I get to one Error code 1 message & 1 Error code 3 message momentarily.

      So here's my questions:
      First, to prevent deadlock, is it at all possible to program the coordinator in Arduino-C to tell each router to "wait"? Or can I do that straight from X-CTU?
      Second, what is the possible cause of the Error codes coming up when I only have only one of the routers connected? When I have the coordinator (in API mode) & one of the routers (in AT mode) connected to the network, it still displays Error code 1 & 3. The great thing is that the network displays those error print statements for short periods of time & goes back to normal, displaying the data as it should. I think both routers are walking all over each.

      Delete
    3. Error code 1 is a bad checksum and error code 3 is an unexpected start byte. So it sounds like you're using API mode 1 instead of API mode 2. Check your configuration, it's the AP command and it should be set to 2. In XCTU it's under the serial interface.

      The XBees don't deadlock. They may well transmit over the top of one another, but they automatically retry to take care of that. On an incredibly busy network, the collision rate can get high, but they do their best to correct for it. The way it works is they enable the radio and listen for a quiet spot and then try to transmit, if it gets clobbered by some other device, they will do it again. The times that they wait are semi random, so they will recover really well. There's no locking to worry about.

      Delete
  21. I just want to say Thank-you, Thank-you, Thank-you! I read both books "Building Wireless Sensor Networks" and "The Hand-on Xbee Lab Manual" and I learned far more from your site than both books combined regarding using the Arduino, Xbee, and the Xbee library. You explained everything in great details.

    You should consider writing a book about it and include all the projects that you have done. I would be the first one to buy it.

    I have a question. When you setup a wireless sensor network about 10 nodes or more, how much delay would you recommend to put in each node when sending a package to the coordinator? I found out that sending a package every 10-15 seconds for each node would reduce the traffic greatly and without errors.

    ReplyDelete
    Replies
    1. I use different delays for different devices. For example, I use 5 seconds in power measurements so I can catch some of the appliances when they cycle. Clothes dryers operate the heating element in short bursts and can be hard to catch on. The garage doors report every minute or whenever they're told to do something. Thirty seconds for the pool, three minutes for the acid pump. The septic tank is the longest at 3 and a half minutes.

      However, all of them except the pool, will report on command. I send them a packet with something in it like 'status' or '?' and they send back what they've got right now. This is to be able to interrogate the sensors to be sure something happened.

      Yes, cutting back on the reporting interval cuts the traffic way back. Especially since some of my devices are too far away from the controller and have to rely on store forward.

      Delete
    2. Regarding report on command. I am sending the value of XBee SL address as a string "0x40ade9b5" via the serial port. How can I pass this value as a parameter into XBeeAddress64 addr64 = XBeeAddress64(0x0013a200, x) since x is in hex.

      Do you have an example code of report on command?

      Delete
    3. Actually, there's several examples of this kind of thing on this blog. For example, the post: http://www.desert-home.com/2013/02/using-xbee-library-part-3.html uses the hex addresses you're wondering about. The way I get it to report on command is to send a command such as 'status' to the device and let the attached processor decode it and respond.

      If you don't have an attached processor, just using the XBee alone, you have to wait for the XBee to time out and send on its own. Almost all of my homemade devices have an attached processor.

      Delete
    4. Dave, I stop using broadcast altogether because it required too much processing and bandwidth. It slows down everything. Right now, I am using report on command only. Each router will send the report to the coordinator and end devices will send the report to one of the routers or the coordinator.

      What I did was very simple. I wrote a Windows program (controller) that communicates through the serial port sending the node address and command.

      For example I would send a string of "0x40b77a1b,LightOn" through the serial port. Assume hexAddress = "0x40b77a1b" and the command = "LightOn" The code would be:

      // convert hex string to long integer
      long decAddress = (long) strtol(hexAddress, NULL, 0);

      // Pass the address of the XBee that you want to send the command
      XBeeAddress64 nodeAddress = XBeeAddress64(0x0013a200, decAddress);

      // Send the command. From your example. I love what you done here.
      ZBTxRequest zbtx = ZBTxRequest(nodeAddress, (uint8_t *)command, strlen(command));

      xbee.send(zbtx);
      delay(10);

      How easy is that!

      Delete
    5. Cool. You do realize I'm totally going to steal your idea.

      Delete
    6. I have a little problem. Since I no longer use broadcast, my routers only send the packet to the coordinator. The coordinator's SL address is already preprogrammed into each router. If the coordinator's radio dies, I would have to reprogram all the routers again. There must be a solution whereby I can just change the coordinator's radio without having to reprogram the routers. What are your thoughts?

      Delete
  22. First you don't have to use the coordinator as the central destination; the coordinator can be just another device on the network. My coordinator is my satellite clock that stays up in the attic gathering the time and coordinating the network. My automation controller is just another router, but I have all the devices send to it. We tend to think that the coordinator has to be the central control point, but it doesn't. That means you aren't constantly taking it off line running the risk of breaking it or messing up the programming. Heck, it can be an XBee hooked to a wall wart plugged into the wall somewhere, it doesn't need to do anything else.

    Next, the trick ( I learned this relatively recently) is to let the XBee take care of addressing. By programming the address you want to send to into the XBee instead of the processor code, you simply change the address in the XBee and you're ready to go. This depends on reading the destination address out of the XBee in your code when you're using code that always sets the address. You can get the DL with an AT command. If the code just uses the address in the XBee, you can skip reading it and just use it.

    I did this several times by using the remote configuration capabilities of XCTU. I simply changed the destination address in the XBee remotely. I don't think I ever did a post on this capability of XCTU, but it's a really nice feature.

    Another way is to have the processor get the address it's supposed to send to when it first boots up. I did this once by having the device that wanted the data send its address in a broadcast every 10-15 seconds. Each device, on boot, just listens until it hears the message and then sends its data to that address.

    Also, if you set the network up in a flexible fashion, the routers can all set themselves up to a new controller in just a few minutes. I discovered this when I screwed up my entire network trying out some new techniques. I posted about it here: http://www.desert-home.com/2014_01_01_archive.html

    So, pick whichever combination of techniques you feel will best work for you and try them out. I like the flexible network with the automation controller (not the XBee controller) sending its address over the network repeatedly. This also allows you to be sure any particular XBee can see the automation controller by simply watching for the address message. In network jargon, this is called a 'beacon'. I haven't posted about this technique either; glad you brought it up.

    ReplyDelete
  23. Dave, thanks for the suggestions, but I am a little bit confused. If you program the address you want to send to into the XBee instead of the processor code, then what do you need to do in the processor code to send the packet?

    XBee library requires a 64bit address so that it knows where to send the package. What would you do in the //Specify the address of the remote XBee? Do you leave it at 0x0 for the SH and 0x0000ffff for the SL? Will XBee uses the processor code SL address or the address that you already programmed into the XBee?

    // Specify the address of the remote XBee (this is the SH + SL)
    XBeeAddress64 addr64 = XBeeAddress64(0x0013a200, 0x403e0f30);

    // Create a TX Request
    ZBTxRequest zbTx = ZBTxRequest(addr64, payload, sizeof(payload));

    // Send your request
    xbee.send(zbTx);

    ReplyDelete
  24. You're right, I used that technique when I was still using broadcast between the devices. When I went to API mode on all of them I put in code to read the DL from the XBee on boot up and saved it to stick in the packets. I sort of used the XBee as non-volatile storage of the destination address. I could then change the address remotely with XCTU, send a command for the arduino to reset and it would come up, read the address and use it. Later, I realized that I could simply send a new address in a message; something like "sendto 403e0f30" and I could change the destination address without having to reset the arduino.

    Since I did it several different ways over time, I get them mixed up.

    On XBees that only have sensors attached, I simply change the DL address using XCTU since there is no other processor involved. I do this with my battery monitors and outdoor temperature sensor.

    ReplyDelete
  25. Dave, I thought you might be interested in these devices.

    1. Connect anything anywhere. http://www.sodaq.net/#!getting-started/c21ma

    1. XBee controlled smart outlet http://www.seeedstudio.com/depot/Smarthome-Kit-Smart-Outlet-p-1105.html

    2. Generic gateway for internet of things. http://www.seeedstudio.com/depot/Dragrove-Generic-gateway-for-internet-of-things-p-1118.html

    ReplyDelete
    Replies
    1. Thank you. The outlet is discontinued darn it, but at $50, not the best price for a single external device. The Sodaq is pretty tempting. I'd have to buy a solar cell and lipo battery to use it. I wonder why they put thermometers on boards that are going to be inside an enclosure; seems to defeat the purpose. The generic gateway is meant to have wires hooked to it. Isn't that a bit silly in the 21st century?

      But, they are all compelling products. It shows that people are starting to think seriously about this kind of thing.

      Thanks again for the pointers.

      Delete
  26. Hi Dave,

    I am trying to send GPS data from one XBee to another using Arduino Mega and Uno. Both XBee's are in API mode and Series 2 version. The problem is that GPS returns results in double but payload in my program accepts int only. You can see some fragments of the code below:

    XBeeAddress64 addr64 = XBeeAddress64(0x0013A200, 0x40ADD02C);
    ZBTxRequest zbTx = ZBTxRequest(addr64, payload, sizeof(payload));
    ZBTxStatusResponse txStatus = ZBTxStatusResponse();

    pin5 = UTC();

    payload[0] = pin5 >> 8 & 0xff;
    payload[1] = pin5 & 0xff;

    xbee.send(zbTx);

    I have a UTC() method which returns local time in double. So, can you please tell me how to send data other than int in API mode.

    ReplyDelete
    Replies
    1. I did a post in response to this particular problem here: http://www.desert-home.com/2013/10/floats-and-strings-over-serial-link.html . Lots of people have this problem.

      Delete
    2. Thanks Dave this is really helpful. But what about sending it? You have mentioned in " Floats and Strings Over a Serial Link (like an XBee) " section that - "The middle part where the string is sent between two devices can be found in other places on this blog." . So can you give me proper link for this??

      Delete
    3. I cover that in detail in several places take a look at my XBee page. It's the tab up at the top labeled "The World of XBee". Look at the posts on the XBee library.

      Delete