/* Tim Sharpe, 8 Apr 2009. This is free software and comes with no warranty whatsoever. This is an Arduino sketch that uses Andrew Rapp's XBee-Arduino Zigbee library. The sketch combinescode from his Zigbee receive and transmit sketch (ZBRxExample and ZBTxExample) to create a single sketch that data from a received packet back to the node that sent it. Mr. Rapp's sketches were helpful, but didn't explain how to extract data from a received packet and use the data create a response to the sending node. This sketch is an attempt to demonstrate how to accomplish this and is commented to expalin some of the "whys". I don't pronise it's a great solution, but it works (at least for me). I developed this sketch using the Ardunio 0014 IDE, an Adafruit Boarduino (Arduino clone that works nicely with solderless breadboards) and a Series 2 XBee Module (ZNET 2.5) running "Router/End Device API" firmware running in ATAP2 API mode per Mr. Rapp's Web Site. I tested the setup using another Series 2 XBee Module (ZNET 2.5) running "Router/End Device AT" firmware, connected to a terminal emulator and configured with the API-mode Xbee as its Destination Node. The goal was to enter characters or strings into the terminal emulator and have the Arduino/API-mode Xbee echo the data back to the terminal emulator. This was a test to understand Mr. Rapp's library well enough to create an API-mode, fully-networked (vice modem type communication) Zigbee application. Portions of this sketch were copied from Mr. Rapp's ZBRxExample and ZBTxExample sketches and modified to function as desired. I used Mr. Rapp's XBee-Arduino Zigbee library (v0.1.1) was used without modification. As Mr. Rapp has GPL'd his sample sketches, please note his GPL license info below. I hope you find this as useful as I did for my projects. */ /** * Copyright (c) 2009 Andrew Rapp. All rights reserved. * * This file is part of XBee-Arduino. * * XBee-Arduino is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * XBee-Arduino is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with XBee-Arduino. If not, see . */ //We're going to need string handling to feed received data back to the transmit object. #include //Mr. Rapp's library. I put it in quotes as I dropped XBee.h and XBee.cpp in the sketch folder // rather than storing it with the Arduino libraries. #include "XBee.h" //Using a software serial port to monitor how the sketch interprets received packets. Just listen // on Arduino pin D2 and 9600bps #include #define rxPin 2 #define txPin 3 SoftwareSerial mySerial = SoftwareSerial(rxPin, txPin); /* Receives a ZB RX packet and sets a PWM value based on packet data. Error led is flashed if an unexpected packet is received */ //This instantiates the XBee library XBee xbee = XBee(); //This creates object instance "response" to process Xbee Packets XBeeResponse response = XBeeResponse(); //This creates object instance "rx" to process Xbee Series 2 API packets ZBRxResponse rx = ZBRxResponse(); //This creates object instance "msr" to process associate/disassociate packets (PAN membership) ModemStatusResponse msr = ModemStatusResponse(); //This creates object instance "txStatus" to process acknowledgements for sent Xbee Series 2 API packets ZBTxStatusResponse txStatus = ZBTxStatusResponse(); //Declare some variables we'll need to hold received packet data so we can use it to transmit //The two 32-bit halves of th4 64-bit address long XBee_Addr64_MS; long XBee_Addr64_LS; //The 16-bit address int XBee_Addr16; //We need a string to hold incoming data bytes from received packets. I chose a 20 byte string as my // transmit needs are short. I suggest you make this at least 3 btes longer than the longest data // block you'll receive. char XBee_Data[20]; //We need to make sure we're not overwriting the string, so we'll name its length to use later #define XBee_Data_len 20 //An integer varailbe for temporary use int dummy; //Mr. Rapp's sketches used LED's on Arduino pins D10, D11, and D12 to indicate activity and status. // I found them quite helpful after reading all the code where they're triggered. I used small LED's // connected to the ATMEGA168 through 1K resisitors. int statusLed = 11; int errorLed = 12; int dataLed = 10; //We're going to need a counter to bring in received data byte-by-byte (how the library delivers it) int count=0; //Mr. Rapp uses this function to flash the LED's in certain ways to indicate different conditions. void flashLed(int pin, int times, int wait) { for (int i = 0; i < times; i++) { digitalWrite(pin, HIGH); delay(wait); digitalWrite(pin, LOW); if (i + 1 < times) { delay(wait); } } } void setup() { //Set up and start the Software Serial port pinMode(rxPin, INPUT); pinMode(txPin, OUTPUT); // set the data rate for the SoftwareSerial port mySerial.begin(9600); //Set up the LED indicator pins pinMode(statusLed, OUTPUT); pinMode(errorLed, OUTPUT); pinMode(dataLed, OUTPUT); //The Xbee Library starts the hardware serial port. xbee.begin(9600); //This just signals that the setup phase is complete. flashLed(statusLed, 3, 50); } // continuously reads packets, looking for ZB Receive or Modem Status void loop() { //Attempt to read an API packet from the ZBee Module xbee.readPacket(); //If we found a packet... if (xbee.getResponse().isAvailable()) { // got something //If it's a Zigbee Receive packet (API ID 0x90)... if (xbee.getResponse().getApiId() == ZB_RX_RESPONSE) { // got a zb rx packet //Populate our "rx" object with info from the received packet (Data, Addresses, etc) xbee.getResponse().getZBRxResponse(rx); //Flash an LED if receipt was acknowleged to the sending node, or maybe not if (rx.getOption() == ZB_PACKET_ACKNOWLEDGED) { // the sender got an ACK flashLed(statusLed, 1, 10); } else { // we got it (obviously) but sender didn't get an ACK flashLed(errorLed, 2, 20); } // set dataLed PWM to value of the first byte in the data analogWrite(dataLed, rx.getData(0)); //Here I'm extracting some of the information I'll need to create a packet to reply back to the // sender. //This one obtains the upper 32-bit word of the 64-bit address. The 64-bit address is the 802.15.4 MAC // layer address (i.e, the "burned in" one). XBee_Addr64_MS=(uint32_t(rx.getFrameData()[0]) << 24) + (uint32_t(rx.getFrameData()[1]) << 16) + (uint16_t(rx.getFrameData()[2]) << 8) + rx.getFrameData()[3]; //This one obtains the lower 32-bit word... XBee_Addr64_LS=(uint32_t(rx.getFrameData()[4]) << 24) + (uint32_t(rx.getFrameData()[5]) << 16) + (uint16_t(rx.getFrameData()[6]) << 8) + rx.getFrameData()[7]; //Send the two parts of the address to the software serial port mySerial.print("Addr64 MS: "); mySerial.print(XBee_Addr64_MS,HEX); mySerial.print('\n'); mySerial.print("Addr64 LS: "); mySerial.print(XBee_Addr64_LS,HEX); mySerial.print('\n'); //Now we extract the 16-bit address. This is the Zigbee Network address of the node in the PAN, analogous to // an IP address in TCP/IP. XBee_Addr16=rx.getRemoteAddress16(); mySerial.print("Addr16: "); //I'm not displaying the extracted result as the Arduino print "HEX" represenation prepends FFFF to the front of the address. // You can comment the rx.getRemoteAddress16() line and uncomment the XBee_Addr16 and see for yourself. Only the output is // wrong. The address is correctly extracted. //mySerial.print(XBee_Addr16,HEX); mySerial.print(rx.getRemoteAddress16(),HEX); mySerial.print('\n'); //We're just going to display the number of bytes of data we'll receive, but not extract it. mySerial.print("DataLength: "); mySerial.print(rx.getDataLength(),DEC); mySerial.print('\n'); //Need to make sure we don't overrun the string. Enforce it's length if (rx.getDataLength()>=XBee_Data_len) {dummy=XBee_Data_len;} else {dummy=rx.getDataLength();} //Now we read the data. Note that we read from the start of the buffer to position DataLength-1. mySerial.print("Data: "); for (count=0;count<=dummy-1;count++) { mySerial.print(rx.getData(count)); XBee_Data[count]=rx.getData(count); } //We may be sending back less data than the length of the XBee_Data string. We'll send the data by // providing a string and a data length that will work just like the way we read the data before. In // order to only send the actual received data back, we need to indicate where the data ends. I did this // by appending a NULL at position DataLength. This makes the strlen function see a string that looks shorter // than the original declared string. C uses "Null-terminated strings", remember? XBee_Data[dummy]='\0'; mySerial.print('\n'); //Now we'll send data what we received to the node that sent it //Create a 64-bit address data structure from the extracted address XBeeAddress64 XBee_Addr64 = XBeeAddress64(XBee_Addr64_MS, XBee_Addr64_LS); //Build the Packet. Note the following (in order of ZBTxRequest arguments): // -The first one is the 64-bit address data structure we just created // -The second one is the 16-bit address // -The next one defines how many nodes a broadcast can pass through while looking for the destination node. I'm // using the defined Zigbee maximum network radius so we have a number as I don't have any reason to make it shorter. // -The next one says this is a transmit Unicast packet, as opposed to a broadcast or multicast // -Next is the data to be sent in the packet. ZBTxRequest requires a uint8_t* string pointer, but strlen // only works with "char" strings. I made a "char" string and am "casting" it as uint8_t* for the library // -The next one tells the library how many bytes of the data string will be sent. This is why I did the // NULL termination after getting the last recevied packet - So I could use "strlen" to provide the length of // data up to the inserted NULL. // -The last one tells the library to increment the FrameID before sending the packet. //Note that we didn't declare the "zbTX" object up front with the other objects. Unlike the others that can be reused, we // have to feed this one new data every time we use it, so we create it on the fly each time. ZBTxRequest zbTx = ZBTxRequest(XBee_Addr64, XBee_Addr16, ZB_BROADCAST_RADIUS_MAX_HOPS, ZB_TX_UNICAST, (uint8_t*) XBee_Data, strlen(XBee_Data), xbee.getNextFrameId()); //Send the packet xbee.send(zbTx); // flash TX indicator flashLed(statusLed, 1, 100); //Now we figure out if the destination node received the packet... // after sending a tx request, we expect a status response // wait up to half second for the status response if (xbee.readPacket(500)) { // got a response! // should be a znet tx status if (xbee.getResponse().getApiId() == ZB_TX_STATUS_RESPONSE) { xbee.getResponse().getZBTxStatusResponse(txStatus); // get the delivery status, the fifth byte if (txStatus.getDeliveryStatus() == SUCCESS) { // success. time to celebrate flashLed(statusLed, 5, 50); } else { // the remote XBee did not receive our packet. is it powered on? flashLed(errorLed, 3, 500); } } } else { // local XBee did not provide a timely TX Status Response -- should not happen flashLed(errorLed, 2, 50); } //*****END OF DATA PACKET PROCESSING****** //*****This part detects associate/disassociate packets***** } else if (xbee.getResponse().getApiId() == MODEM_STATUS_RESPONSE) { xbee.getResponse().getModemStatusResponse(msr); // the local XBee sends this response on certain events, like association/dissociation if (msr.getStatus() == ASSOCIATED) { // yay this is great. flash led flashLed(statusLed, 10, 10); } else if (msr.getStatus() == DISASSOCIATED) { // this is awful.. flash led to show our discontent flashLed(errorLed, 10, 10); } else { // another status flashLed(statusLed, 5, 10); } } else { // not something we were expecting flashLed(errorLed, 1, 25); } } }