XBee Library in API mode. Based on xbee-arduino.

Dependencies:   mbed

Dependents:   XBee_API_Demo FinalProject FootModule HandController ... more

Revision:
0:350d308e7b77
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/XBee.cpp	Sun Apr 23 19:58:12 2017 +0000
@@ -0,0 +1,1772 @@
+/**
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include "XBee.h"
+
+XBeeResponse::XBeeResponse() {
+
+}
+
+uint8_t XBeeResponse::getApiId() {
+	return _apiId;
+}
+
+void XBeeResponse::setApiId(uint8_t apiId) {
+	_apiId = apiId;
+}
+
+uint8_t XBeeResponse::getMsbLength() {
+	return _msbLength;
+}
+
+void XBeeResponse::setMsbLength(uint8_t msbLength) {
+	_msbLength = msbLength;
+}
+
+uint8_t XBeeResponse::getLsbLength() {
+	return _lsbLength;
+}
+
+void XBeeResponse::setLsbLength(uint8_t lsbLength) {
+	_lsbLength = lsbLength;
+}
+
+uint8_t XBeeResponse::getChecksum() {
+	return _checksum;
+}
+
+void XBeeResponse::setChecksum(uint8_t checksum) {
+	_checksum = checksum;
+}
+
+uint8_t XBeeResponse::getFrameDataLength() {
+	return _frameLength;
+}
+
+void XBeeResponse::setFrameLength(uint8_t frameLength) {
+	_frameLength = frameLength;
+}
+
+bool XBeeResponse::isAvailable() {
+	return _complete;
+}
+
+void XBeeResponse::setAvailable(bool complete) {
+	_complete = complete;
+}
+
+bool XBeeResponse::isError() {
+	return _errorCode > 0;
+}
+
+uint8_t XBeeResponse::getErrorCode() {
+	return _errorCode;
+}
+
+void XBeeResponse::setErrorCode(uint8_t errorCode) {
+	_errorCode = errorCode;
+}
+
+// copy common fields from xbee response to target response
+void XBeeResponse::setCommon(XBeeResponse &target) {
+	target.setApiId(getApiId());
+	target.setAvailable(isAvailable());
+	target.setChecksum(getChecksum());
+	target.setErrorCode(getErrorCode());
+	target.setFrameLength(getFrameDataLength());
+	target.setMsbLength(getMsbLength());
+	target.setLsbLength(getLsbLength());
+}
+
+#ifdef SERIES_2
+
+ZBTxStatusResponse::ZBTxStatusResponse() : FrameIdResponse() {
+
+}
+
+uint16_t ZBTxStatusResponse::getRemoteAddress() {
+	return  (getFrameData()[1] << 8) + getFrameData()[2];
+}
+
+uint8_t ZBTxStatusResponse::getTxRetryCount() {
+	return getFrameData()[3];
+}
+
+uint8_t ZBTxStatusResponse::getDeliveryStatus() {
+	return getFrameData()[4];
+}
+
+uint8_t ZBTxStatusResponse::getDiscoveryStatus() {
+	return getFrameData()[5];
+}
+
+bool ZBTxStatusResponse::isSuccess() {
+	return getDeliveryStatus() == SUCCESS;
+}
+
+void XBeeResponse::getZBTxStatusResponse(XBeeResponse &zbXBeeResponse) {
+
+	// way off?
+	ZBTxStatusResponse* zb = static_cast<ZBTxStatusResponse*>(&zbXBeeResponse);
+	// pass pointer array to subclass
+	zb->setFrameData(getFrameData());
+	setCommon(zbXBeeResponse);
+}
+
+ZBRxResponse::ZBRxResponse(): RxDataResponse() {
+	_remoteAddress64 = XBeeAddress64();
+}
+
+uint16_t ZBRxResponse::getRemoteAddress16() {
+	return 	(getFrameData()[8] << 8) + getFrameData()[9];
+}
+
+uint8_t ZBRxResponse::getOption() {
+	return getFrameData()[10];
+}
+
+// markers to read data from packet array.  this is the index, so the 12th item in the array
+uint8_t ZBRxResponse::getDataOffset() {
+	return 11;
+}
+
+uint8_t ZBRxResponse::getDataLength() {
+	return getPacketLength() - getDataOffset() - 1;
+}
+
+XBeeAddress64& ZBRxResponse::getRemoteAddress64() {
+	return _remoteAddress64;
+}
+
+void XBeeResponse::getZBRxResponse(XBeeResponse &rxResponse) {
+
+	ZBRxResponse* zb = static_cast<ZBRxResponse*>(&rxResponse);
+
+	//TODO verify response api id matches this api for this response
+
+	// pass pointer array to subclass
+	zb->setFrameData(getFrameData());
+	setCommon(rxResponse);
+
+	zb->getRemoteAddress64().setMsb((uint32_t(getFrameData()[0]) << 24) + (uint32_t(getFrameData()[1]) << 16) + (uint16_t(getFrameData()[2]) << 8) + getFrameData()[3]);
+	zb->getRemoteAddress64().setLsb((uint32_t(getFrameData()[4]) << 24) + (uint32_t(getFrameData()[5]) << 16) + (uint16_t(getFrameData()[6]) << 8) + (getFrameData()[7]));
+}
+
+ZBExplicitRxResponse::ZBExplicitRxResponse(): ZBRxResponse() {
+}
+
+uint8_t ZBExplicitRxResponse::getSrcEndpoint() {
+	return getFrameData()[10];
+}
+
+uint8_t ZBExplicitRxResponse::getDstEndpoint() {
+	return getFrameData()[11];
+}
+
+uint16_t ZBExplicitRxResponse::getClusterId() {
+	return (uint16_t)(getFrameData()[12]) << 8 | getFrameData()[13];
+}
+
+uint16_t ZBExplicitRxResponse::getProfileId() {
+	return (uint16_t)(getFrameData()[14]) << 8 | getFrameData()[15];
+}
+
+uint8_t ZBExplicitRxResponse::getOption() {
+	return getFrameData()[16];
+}
+
+// markers to read data from packet array.
+uint8_t ZBExplicitRxResponse::getDataOffset() {
+	return 17;
+}
+
+uint8_t ZBExplicitRxResponse::getDataLength() {
+	return getPacketLength() - getDataOffset() - 1;
+}
+
+void XBeeResponse::getZBExplicitRxResponse(XBeeResponse &rxResponse) {
+	// Nothing to add to that
+	getZBRxResponse(rxResponse);
+}
+
+
+ZBRxIoSampleResponse::ZBRxIoSampleResponse() : ZBRxResponse() {
+
+}
+
+// 64 + 16 addresses, sample size, option = 12 (index 11), so this starts at 12
+uint8_t ZBRxIoSampleResponse::getDigitalMaskMsb() {
+	return getFrameData()[12] & 0x1c;
+}
+
+uint8_t ZBRxIoSampleResponse::getDigitalMaskLsb() {
+	return getFrameData()[13];
+}
+
+uint8_t ZBRxIoSampleResponse::getAnalogMask() {
+	return getFrameData()[14] & 0x8f;
+}
+
+bool ZBRxIoSampleResponse::containsAnalog() {
+	return getAnalogMask() > 0;
+}
+
+bool ZBRxIoSampleResponse::containsDigital() {
+	return getDigitalMaskMsb() > 0 || getDigitalMaskLsb() > 0;
+}
+
+bool ZBRxIoSampleResponse::isAnalogEnabled(uint8_t pin) {
+	return ((getAnalogMask() >> pin) & 1) == 1;
+}
+
+bool ZBRxIoSampleResponse::isDigitalEnabled(uint8_t pin) {
+	if (pin <= 7) {
+		// added extra parens to calm avr compiler
+		return ((getDigitalMaskLsb() >> pin) & 1) == 1;
+	} else {
+		return ((getDigitalMaskMsb() >> (pin - 8)) & 1) == 1;
+	}
+}
+
+uint16_t ZBRxIoSampleResponse::getAnalog(uint8_t pin) {
+	// analog starts 13 bytes after sample size, if no dio enabled
+	uint8_t start = 15;
+
+	if (containsDigital()) {
+		// make room for digital i/o
+		start+=2;
+	}
+
+	// start depends on how many pins before this pin are enabled
+	for (int i = 0; i < pin; i++) {
+		if (isAnalogEnabled(i)) {
+			start+=2;
+		}
+	}
+
+	return (uint16_t)((getFrameData()[start] << 8) + getFrameData()[start + 1]);
+}
+
+bool ZBRxIoSampleResponse::isDigitalOn(uint8_t pin) {
+	if (pin <= 7) {
+		// D0-7
+		// DIO LSB is index 5
+		return ((getFrameData()[16] >> pin) & 1) == 1;
+	} else {
+		// D10-12
+		// DIO MSB is index 4
+		return ((getFrameData()[15] >> (pin - 8)) & 1) == 1;
+	}
+}
+
+void XBeeResponse::getZBRxIoSampleResponse(XBeeResponse &response) {
+	ZBRxIoSampleResponse* zb = static_cast<ZBRxIoSampleResponse*>(&response);
+
+
+	// pass pointer array to subclass
+	zb->setFrameData(getFrameData());
+	setCommon(response);
+
+	zb->getRemoteAddress64().setMsb((uint32_t(getFrameData()[0]) << 24) + (uint32_t(getFrameData()[1]) << 16) + (uint16_t(getFrameData()[2]) << 8) + getFrameData()[3]);
+	zb->getRemoteAddress64().setLsb((uint32_t(getFrameData()[4]) << 24) + (uint32_t(getFrameData()[5]) << 16) + (uint16_t(getFrameData()[6]) << 8) + (getFrameData()[7]));
+}
+
+#endif
+
+#ifdef SERIES_1
+
+RxResponse::RxResponse() : RxDataResponse() {
+
+}
+
+uint16_t Rx16Response::getRemoteAddress16() {
+	return (getFrameData()[0] << 8) + getFrameData()[1];
+}
+
+XBeeAddress64& Rx64Response::getRemoteAddress64() {
+	return _remoteAddress;
+}
+
+Rx64Response::Rx64Response() : RxResponse() {
+	_remoteAddress = XBeeAddress64();
+}
+
+Rx16Response::Rx16Response() : RxResponse() {
+
+}
+
+RxIoSampleBaseResponse::RxIoSampleBaseResponse() : RxResponse() {
+
+}
+
+uint8_t RxIoSampleBaseResponse::getSampleOffset() {
+	// sample starts 2 bytes after rssi
+	return getRssiOffset() + 2;
+}
+
+
+uint8_t RxIoSampleBaseResponse::getSampleSize() {
+	return getFrameData()[getSampleOffset()];
+}
+
+bool RxIoSampleBaseResponse::containsAnalog() {
+	return (getFrameData()[getSampleOffset() + 1] & 0x7e) > 0;
+}
+
+bool RxIoSampleBaseResponse::containsDigital() {
+	return (getFrameData()[getSampleOffset() + 1] & 0x1) > 0 || getFrameData()[getSampleOffset() + 2] > 0;
+}
+
+//uint16_t RxIoSampleBaseResponse::getAnalog0(uint8_t sample) {
+//	return getAnalog(0, sample);
+//}
+
+bool RxIoSampleBaseResponse::isAnalogEnabled(uint8_t pin) {
+	return (((getFrameData()[getSampleOffset() + 1] >> (pin + 1)) & 1) == 1);
+}
+
+bool RxIoSampleBaseResponse::isDigitalEnabled(uint8_t pin) {
+	if (pin < 8) {
+		return ((getFrameData()[getSampleOffset() + 2] >> pin) & 1) == 1;
+	} else {
+		return (getFrameData()[getSampleOffset() + 1] & 1) == 1;
+	}
+}
+
+//	// verified (from XBee-API)
+//	private int getSampleWidth() {
+//		int width = 0;
+//
+//		// width of sample depends on how many I/O pins are enabled. add one for each analog that's enabled
+//		for (int i = 0; i <= 5; i++) {
+//			if (isAnalogEnabled(i)) {
+//				// each analog is two bytes
+//				width+=2;
+//			}
+//		}
+//
+//		if (this.containsDigital()) {
+//			// digital enabled takes two bytes, no matter how many pins enabled
+//			width+= 2;
+//		}
+//
+//		return width;
+//	}
+//
+//	private int getStartIndex() {
+//
+//		int startIndex;
+//
+//		if (this.getSourceAddress() instanceof XBeeAddress16) {
+//			// 16 bit
+//			startIndex = 7;
+//		} else {
+//			// 64 bit
+//			startIndex = 13;
+//		}
+//
+//		return startIndex;
+//	}
+//
+//	public int getDigitalMsb(int sample) {
+//		// msb digital always starts 3 bytes after sample size
+//		return this.getProcessedPacketBytes()[this.getStartIndex() + 3 + this.getSampleWidth() * sample];
+//	}
+//
+//	public int getDigitalLsb(int sample) {
+//		return this.getProcessedPacketBytes()[this.getStartIndex() + 3 + this.getSampleWidth() * sample + 1];
+//	}
+//
+//	public Boolean isDigitalOn(int pin, int sample) {
+//
+//		if (sample < 0 || sample >= this.getSampleSize()) {
+//			throw new IllegalArgumentException("invalid sample size: " + sample);
+//		}
+//
+//		if (!this.containsDigital()) {
+//			throw new RuntimeException("Digital is not enabled");
+//		}
+//
+//		if (pin >= 0 && pin < 8) {
+//			return ((this.getDigitalLsb(sample) >> pin) & 1) == 1;
+//		} else if (pin == 8) {
+//			// uses msb dio line
+//			return (this.getDigitalMsb(sample) & 1) == 1;
+//		} else {
+//			throw new IllegalArgumentException("Invalid pin: " + pin);
+//		}
+//	}
+//
+//	public Integer getAnalog(int pin, int sample) {
+//
+//		if (sample < 0 || sample >= this.getSampleSize()) {
+//			throw new IllegalArgumentException("invalid sample size: " + sample);
+//		}
+//
+//		// analog starts 3 bytes after start of sample, if no dio enabled
+//		int startIndex = this.getStartIndex() + 3;
+//
+//		if (this.containsDigital()) {
+//			// make room for digital i/o sample (2 bytes per sample)
+//			startIndex+= 2;
+//		}
+//
+//		startIndex+= this.getSampleWidth() * sample;
+//
+//		// start depends on how many pins before this pin are enabled
+//		// this will throw illegalargumentexception if invalid pin
+//		for (int i = 0; i < pin; i++) {
+//			if (isAnalogEnabled(i)) {
+//				startIndex+=2;
+//			}
+//		}
+//
+//		return (this.getProcessedPacketBytes()[startIndex] << 8) + this.getProcessedPacketBytes()[startIndex + 1];
+//	}
+
+uint8_t RxIoSampleBaseResponse::getSampleStart(uint8_t sample) {
+	uint8_t spacing = 0;
+
+	if (containsDigital()) {
+		// make room for digital i/o sample (2 bytes per sample)
+		spacing += 2;
+	}
+
+	// spacing between samples depends on how many are enabled. add
+	// 2 bytes for each analog that's enabled
+	for (int i = 0; i <= 5; i++) {
+		if (isAnalogEnabled(i)) {
+			// each analog is two bytes
+			spacing+=2;
+		}
+	}
+
+	// Skip 3-byte header and "sample" full samples
+	return getSampleOffset() + 3 + sample * spacing;
+}
+
+uint16_t RxIoSampleBaseResponse::getAnalog(uint8_t pin, uint8_t sample) {
+	uint8_t start = getSampleStart(sample);
+
+	if (containsDigital()) {
+		// Skip digital sample info
+		start += 2;
+	}
+
+	// Skip any analog samples before this pin
+	for (int i = 0; i < pin; i++) {
+		if (isAnalogEnabled(i)) {
+			start+=2;
+		}
+	}
+
+	return (uint16_t)((getFrameData()[start] << 8) + getFrameData()[start + 1]);
+}
+
+bool RxIoSampleBaseResponse::isDigitalOn(uint8_t pin, uint8_t sample) {
+	if (pin < 8) {
+		return ((getFrameData()[getSampleStart(sample) + 1] >> pin) & 1) == 1;
+	} else {
+		return (getFrameData()[getSampleStart(sample)] & 1) == 1;
+	}
+}
+
+
+//bool RxIoSampleBaseResponse::isDigital0On(uint8_t sample) {
+//	return isDigitalOn(0, sample);
+//}
+
+Rx16IoSampleResponse::Rx16IoSampleResponse() : RxIoSampleBaseResponse() {
+
+}
+
+uint16_t Rx16IoSampleResponse::getRemoteAddress16() {
+	return (uint16_t)((getFrameData()[0] << 8) + getFrameData()[1]);
+}
+
+uint8_t Rx16IoSampleResponse::getRssiOffset() {
+	return 2;
+}
+
+void XBeeResponse::getRx16IoSampleResponse(XBeeResponse &response) {
+	Rx16IoSampleResponse* rx = static_cast<Rx16IoSampleResponse*>(&response);
+
+	rx->setFrameData(getFrameData());
+	setCommon(response);
+}
+
+
+Rx64IoSampleResponse::Rx64IoSampleResponse() : RxIoSampleBaseResponse() {
+	_remoteAddress = XBeeAddress64();
+}
+
+XBeeAddress64& Rx64IoSampleResponse::getRemoteAddress64() {
+	return _remoteAddress;
+}
+
+uint8_t Rx64IoSampleResponse::getRssiOffset() {
+	return 8;
+}
+
+void XBeeResponse::getRx64IoSampleResponse(XBeeResponse &response) {
+	Rx64IoSampleResponse* rx = static_cast<Rx64IoSampleResponse*>(&response);
+
+	rx->setFrameData(getFrameData());
+	setCommon(response);
+
+	rx->getRemoteAddress64().setMsb((uint32_t(getFrameData()[0]) << 24) + (uint32_t(getFrameData()[1]) << 16) + (uint16_t(getFrameData()[2]) << 8) + getFrameData()[3]);
+	rx->getRemoteAddress64().setLsb((uint32_t(getFrameData()[4]) << 24) + (uint32_t(getFrameData()[5]) << 16) + (uint16_t(getFrameData()[6]) << 8) + getFrameData()[7]);
+}
+
+TxStatusResponse::TxStatusResponse() : FrameIdResponse() {
+
+}
+
+uint8_t TxStatusResponse::getStatus() {
+	return getFrameData()[1];
+}
+
+bool TxStatusResponse::isSuccess() {
+	return getStatus() == SUCCESS;
+}
+
+void XBeeResponse::getTxStatusResponse(XBeeResponse &txResponse) {
+
+	TxStatusResponse* txStatus = static_cast<TxStatusResponse*>(&txResponse);
+
+	// pass pointer array to subclass
+	txStatus->setFrameData(getFrameData());
+	setCommon(txResponse);
+}
+
+uint8_t RxResponse::getRssi() {
+	return getFrameData()[getRssiOffset()];
+}
+
+uint8_t RxResponse::getOption() {
+	return getFrameData()[getRssiOffset() + 1];
+}
+
+bool RxResponse::isAddressBroadcast() {
+	return (getOption() & 2) == 2;
+}
+
+bool RxResponse::isPanBroadcast() {
+	return (getOption() & 4) == 4;
+}
+
+uint8_t RxResponse::getDataLength() {
+	return getPacketLength() - getDataOffset() - 1;
+}
+
+uint8_t RxResponse::getDataOffset() {
+	return getRssiOffset() + 2;
+}
+
+uint8_t Rx16Response::getRssiOffset() {
+	return RX_16_RSSI_OFFSET;
+}
+
+void XBeeResponse::getRx16Response(XBeeResponse &rx16Response) {
+
+	Rx16Response* rx16 = static_cast<Rx16Response*>(&rx16Response);
+
+	// pass pointer array to subclass
+	rx16->setFrameData(getFrameData());
+	setCommon(rx16Response);
+}
+
+uint8_t Rx64Response::getRssiOffset() {
+	return RX_64_RSSI_OFFSET;
+}
+
+void XBeeResponse::getRx64Response(XBeeResponse &rx64Response) {
+
+	Rx64Response* rx64 = static_cast<Rx64Response*>(&rx64Response);
+
+	// pass pointer array to subclass
+	rx64->setFrameData(getFrameData());
+	setCommon(rx64Response);
+
+	rx64->getRemoteAddress64().setMsb((uint32_t(getFrameData()[0]) << 24) + (uint32_t(getFrameData()[1]) << 16) + (uint16_t(getFrameData()[2]) << 8) + getFrameData()[3]);
+	rx64->getRemoteAddress64().setLsb((uint32_t(getFrameData()[4]) << 24) + (uint32_t(getFrameData()[5]) << 16) + (uint16_t(getFrameData()[6]) << 8) + getFrameData()[7]);
+}
+
+#endif
+
+RemoteAtCommandResponse::RemoteAtCommandResponse() : AtCommandResponse() {
+
+}
+
+uint8_t* RemoteAtCommandResponse::getCommand() {
+	return getFrameData() + 11;
+}
+
+uint8_t RemoteAtCommandResponse::getStatus() {
+	return getFrameData()[13];
+}
+
+bool RemoteAtCommandResponse::isOk() {
+	// weird c++ behavior.  w/o this method, it calls AtCommandResponse::isOk(), which calls the AtCommandResponse::getStatus, not this.getStatus!!!
+	return getStatus() == AT_OK;
+}
+
+uint8_t RemoteAtCommandResponse::getValueLength() {
+	return getFrameDataLength() - 14;
+}
+
+uint8_t* RemoteAtCommandResponse::getValue() {
+	if (getValueLength() > 0) {
+		// value is only included for query commands.  set commands does not return a value
+		return getFrameData() + 14;
+	}
+
+	return NULL;
+}
+
+uint16_t RemoteAtCommandResponse::getRemoteAddress16() {
+	return uint16_t((getFrameData()[9] << 8) + getFrameData()[10]);
+}
+
+XBeeAddress64& RemoteAtCommandResponse::getRemoteAddress64() {
+	return _remoteAddress64;
+}
+
+void XBeeResponse::getRemoteAtCommandResponse(XBeeResponse &response) {
+
+	// TODO no real need to cast.  change arg to match expected class
+	RemoteAtCommandResponse* at = static_cast<RemoteAtCommandResponse*>(&response);
+
+	// pass pointer array to subclass
+	at->setFrameData(getFrameData());
+	setCommon(response);
+
+	at->getRemoteAddress64().setMsb((uint32_t(getFrameData()[1]) << 24) + (uint32_t(getFrameData()[2]) << 16) + (uint16_t(getFrameData()[3]) << 8) + getFrameData()[4]);
+	at->getRemoteAddress64().setLsb((uint32_t(getFrameData()[5]) << 24) + (uint32_t(getFrameData()[6]) << 16) + (uint16_t(getFrameData()[7]) << 8) + (getFrameData()[8]));
+
+}
+
+RxDataResponse::RxDataResponse() : XBeeResponse() {
+
+}
+
+uint8_t RxDataResponse::getData(int index) {
+	return getFrameData()[getDataOffset() + index];
+}
+
+uint8_t* RxDataResponse::getData() {
+	return getFrameData() + getDataOffset();
+}
+
+FrameIdResponse::FrameIdResponse() {
+
+}
+
+uint8_t FrameIdResponse::getFrameId() {
+	return getFrameData()[0];
+}
+
+
+ModemStatusResponse::ModemStatusResponse() {
+
+}
+
+uint8_t ModemStatusResponse::getStatus() {
+	return getFrameData()[0];
+}
+
+void XBeeResponse::getModemStatusResponse(XBeeResponse &modemStatusResponse) {
+
+	ModemStatusResponse* modem = static_cast<ModemStatusResponse*>(&modemStatusResponse);
+
+	// pass pointer array to subclass
+	modem->setFrameData(getFrameData());
+	setCommon(modemStatusResponse);
+
+}
+
+AtCommandResponse::AtCommandResponse() {
+
+}
+
+uint8_t* AtCommandResponse::getCommand() {
+	return getFrameData() + 1;
+}
+
+uint8_t AtCommandResponse::getStatus() {
+	return getFrameData()[3];
+}
+
+uint8_t AtCommandResponse::getValueLength() {
+	return getFrameDataLength() - 4;
+}
+
+uint8_t* AtCommandResponse::getValue() {
+	if (getValueLength() > 0) {
+		// value is only included for query commands.  set commands does not return a value
+		return getFrameData() + 4;
+	}
+
+	return NULL;
+}
+
+bool AtCommandResponse::isOk() {
+	return getStatus() == AT_OK;
+}
+
+void XBeeResponse::getAtCommandResponse(XBeeResponse &atCommandResponse) {
+
+	AtCommandResponse* at = static_cast<AtCommandResponse*>(&atCommandResponse);
+
+	// pass pointer array to subclass
+	at->setFrameData(getFrameData());
+	setCommon(atCommandResponse);
+}
+
+uint16_t XBeeResponse::getPacketLength() {
+	return ((_msbLength << 8) & 0xff) + (_lsbLength & 0xff);
+}
+
+uint8_t* XBeeResponse::getFrameData() {
+	return _frameDataPtr;
+}
+
+void XBeeResponse::setFrameData(uint8_t* frameDataPtr) {
+	_frameDataPtr = frameDataPtr;
+}
+
+void XBeeResponse::init() {
+	_complete = false;
+	_errorCode = NO_ERROR;
+	_checksum = 0;
+}
+
+void XBeeResponse::reset() {
+	init();
+	_apiId = 0;
+	_msbLength = 0;
+	_lsbLength = 0;
+	_checksum = 0;
+	_frameLength = 0;
+
+	_errorCode = NO_ERROR;
+}
+
+void XBee::resetResponse() {
+	_pos = 0;
+	_escape = false;
+	_checksumTotal = 0;
+	_response.reset();
+}
+
+XBee::XBee(RawSerial &serial): _response(XBeeResponse()) {
+        _pos = 0;
+        _escape = false;
+        _checksumTotal = 0;
+        _nextFrameId = 0;
+
+        _response.init();
+        _response.setFrameData(_responseFrameData);
+
+        _serial = &serial;
+}
+
+uint8_t XBee::getNextFrameId() {
+
+	_nextFrameId++;
+
+	if (_nextFrameId == 0) {
+		// can't send 0 because that disables status response
+		_nextFrameId = 1;
+	}
+
+	return _nextFrameId;
+}
+
+void XBee::setSerial(RawSerial &serial) {
+	_serial = &serial;
+}
+
+bool XBee::available() {
+	return _serial->readable();
+}
+
+uint8_t XBee::read() {
+	return _serial->getc();
+}
+
+void XBee::write(uint8_t val) {
+	_serial->putc(val);
+}
+
+XBeeResponse& XBee::getResponse() {
+	return _response;
+}
+
+// TODO how to convert response to proper subclass?
+void XBee::getResponse(XBeeResponse &response) {
+
+	response.setMsbLength(_response.getMsbLength());
+	response.setLsbLength(_response.getLsbLength());
+	response.setApiId(_response.getApiId());
+	response.setFrameLength(_response.getFrameDataLength());
+
+	response.setFrameData(_response.getFrameData());
+}
+
+void XBee::readPacketUntilAvailable() {
+	while (!(getResponse().isAvailable() || getResponse().isError())) {
+		// read some more
+		readPacket();
+	}
+}
+
+bool XBee::readPacket(int timeout) {
+
+	if (timeout < 0) {
+		return false;
+	}
+
+    timer.reset();
+	timer.start();
+
+    while (timer.read_ms() < timeout) {
+
+     	readPacket();
+
+     	if (getResponse().isAvailable()) {
+     		return true;
+     	} else if (getResponse().isError()) {
+     		return false;
+     	}
+    }
+
+    // timed out
+    return false;
+}
+
+void XBee::readPacket() {
+	// reset previous response
+	if (_response.isAvailable() || _response.isError()) {
+		// discard previous packet and start over
+		resetResponse();
+	}
+
+    while (available()) {
+
+        b = read();
+
+        if (_pos > 0 && b == START_BYTE && ATAP == 2) {
+        	// new packet start before previous packeted completed -- discard previous packet and start over
+        	_response.setErrorCode(UNEXPECTED_START_BYTE);
+        	return;
+        }
+
+		if (_pos > 0 && b == ESCAPE) {
+			if (available()) {
+				b = read();
+				b = 0x20 ^ b;
+			} else {
+				// escape byte.  next byte will be
+				_escape = true;
+				continue;
+			}
+		}
+
+		if (_escape == true) {
+			b = 0x20 ^ b;
+			_escape = false;
+		}
+
+		// checksum includes all bytes starting with api id
+		if (_pos >= API_ID_INDEX) {
+			_checksumTotal+= b;
+		}
+
+        switch(_pos) {
+			case 0:
+		        if (b == START_BYTE) {
+		        	_pos++;
+		        }
+
+		        break;
+			case 1:
+				// length msb
+				_response.setMsbLength(b);
+				_pos++;
+
+				break;
+			case 2:
+				// length lsb
+				_response.setLsbLength(b);
+				_pos++;
+
+				break;
+			case 3:
+				_response.setApiId(b);
+				_pos++;
+
+				break;
+			default:
+				// starts at fifth byte
+
+				if (_pos > MAX_FRAME_DATA_SIZE) {
+					// exceed max size.  should never occur
+					_response.setErrorCode(PACKET_EXCEEDS_BYTE_ARRAY_LENGTH);
+					return;
+				}
+
+				// check if we're at the end of the packet
+				// packet length does not include start, length, or checksum bytes, so add 3
+				if (_pos == (_response.getPacketLength() + 3)) {
+					// verify checksum
+
+					if ((_checksumTotal & 0xff) == 0xff) {
+						_response.setChecksum(b);
+						_response.setAvailable(true);
+
+						_response.setErrorCode(NO_ERROR);
+					} else {
+						// checksum failed
+						_response.setErrorCode(CHECKSUM_FAILURE);
+					}
+
+					// minus 4 because we start after start,msb,lsb,api and up to but not including checksum
+					// e.g. if frame was one byte, _pos=4 would be the byte, pos=5 is the checksum, where end stop reading
+					_response.setFrameLength(_pos - 4);
+
+					// reset state vars
+					_pos = 0;
+
+					return;
+				} else {
+					// add to packet array, starting with the fourth byte of the apiFrame
+					_response.getFrameData()[_pos - 4] = b;
+					_pos++;
+				}
+        }
+    }
+}
+
+// it's peanut butter jelly time!!
+
+XBeeRequest::XBeeRequest(uint8_t apiId, uint8_t frameId) {
+	_apiId = apiId;
+	_frameId = frameId;
+}
+
+void XBeeRequest::setFrameId(uint8_t frameId) {
+	_frameId = frameId;
+}
+
+uint8_t XBeeRequest::getFrameId() {
+	return _frameId;
+}
+
+uint8_t XBeeRequest::getApiId() {
+	return _apiId;
+}
+
+void XBeeRequest::setApiId(uint8_t apiId) {
+	_apiId = apiId;
+}
+
+//void XBeeRequest::reset() {
+//	_frameId = DEFAULT_FRAME_ID;
+//}
+
+//uint8_t XBeeRequest::getPayloadOffset() {
+//	return _payloadOffset;
+//}
+//
+//uint8_t XBeeRequest::setPayloadOffset(uint8_t payloadOffset) {
+//	_payloadOffset = payloadOffset;
+//}
+
+
+PayloadRequest::PayloadRequest(uint8_t apiId, uint8_t frameId, uint8_t *payload, uint8_t payloadLength) : XBeeRequest(apiId, frameId) {
+	_payloadPtr = payload;
+	_payloadLength = payloadLength;
+}
+
+uint8_t* PayloadRequest::getPayload() {
+	return _payloadPtr;
+}
+
+void PayloadRequest::setPayload(uint8_t* payload) {
+	_payloadPtr = payload;
+}
+
+uint8_t PayloadRequest::getPayloadLength() {
+	return _payloadLength;
+}
+
+void PayloadRequest::setPayloadLength(uint8_t payloadLength) {
+	_payloadLength = payloadLength;
+}
+
+#ifdef SERIES_2
+
+ZBTxRequest::ZBTxRequest() : PayloadRequest(ZB_TX_REQUEST, DEFAULT_FRAME_ID, NULL, 0) {
+	_addr16 = ZB_BROADCAST_ADDRESS;
+	_broadcastRadius = ZB_BROADCAST_RADIUS_MAX_HOPS;
+	_option = ZB_TX_UNICAST;
+}
+
+ZBTxRequest::ZBTxRequest(const XBeeAddress64 &addr64, uint16_t addr16, uint8_t broadcastRadius, uint8_t option, uint8_t *data, uint8_t dataLength, uint8_t frameId): PayloadRequest(ZB_TX_REQUEST, frameId, data, dataLength) {
+	_addr64 = addr64;
+	_addr16 = addr16;
+	_broadcastRadius = broadcastRadius;
+	_option = option;
+}
+
+ZBTxRequest::ZBTxRequest(const XBeeAddress64 &addr64, uint8_t *data, uint8_t dataLength): PayloadRequest(ZB_TX_REQUEST, DEFAULT_FRAME_ID, data, dataLength) {
+	_addr64 = addr64;
+	_addr16 = ZB_BROADCAST_ADDRESS;
+	_broadcastRadius = ZB_BROADCAST_RADIUS_MAX_HOPS;
+	_option = ZB_TX_UNICAST;
+}
+
+uint8_t ZBTxRequest::getFrameData(uint8_t pos) {
+	if (pos == 0) {
+		return (_addr64.getMsb() >> 24) & 0xff;
+	} else if (pos == 1) {
+		return (_addr64.getMsb() >> 16) & 0xff;
+	} else if (pos == 2) {
+		return (_addr64.getMsb() >> 8) & 0xff;
+	} else if (pos == 3) {
+		return _addr64.getMsb() & 0xff;
+	} else if (pos == 4) {
+		return (_addr64.getLsb() >> 24) & 0xff;
+	} else if (pos == 5) {
+		return  (_addr64.getLsb() >> 16) & 0xff;
+	} else if (pos == 6) {
+		return (_addr64.getLsb() >> 8) & 0xff;
+	} else if (pos == 7) {
+		return _addr64.getLsb() & 0xff;
+	} else if (pos == 8) {
+		return (_addr16 >> 8) & 0xff;
+	} else if (pos == 9) {
+		return _addr16 & 0xff;
+	} else if (pos == 10) {
+		return _broadcastRadius;
+	} else if (pos == 11) {
+		return _option;
+	} else {
+		return getPayload()[pos - ZB_TX_API_LENGTH];
+	}
+}
+
+uint8_t ZBTxRequest::getFrameDataLength() {
+	return ZB_TX_API_LENGTH + getPayloadLength();
+}
+
+XBeeAddress64& ZBTxRequest::getAddress64() {
+	return _addr64;
+}
+
+uint16_t ZBTxRequest::getAddress16() {
+	return _addr16;
+}
+
+uint8_t ZBTxRequest::getBroadcastRadius() {
+	return _broadcastRadius;
+}
+
+uint8_t ZBTxRequest::getOption() {
+	return _option;
+}
+
+void ZBTxRequest::setAddress64(const XBeeAddress64& addr64) {
+	_addr64 = addr64;
+}
+
+void ZBTxRequest::setAddress16(uint16_t addr16) {
+	_addr16 = addr16;
+}
+
+void ZBTxRequest::setBroadcastRadius(uint8_t broadcastRadius) {
+	_broadcastRadius = broadcastRadius;
+}
+
+void ZBTxRequest::setOption(uint8_t option) {
+	_option = option;
+}
+
+
+
+ZBExplicitTxRequest::ZBExplicitTxRequest() : ZBTxRequest() {
+	_srcEndpoint = DEFAULT_ENDPOINT;
+	_dstEndpoint = DEFAULT_ENDPOINT;
+	_profileId = DEFAULT_PROFILE_ID;
+	_clusterId = DEFAULT_CLUSTER_ID;
+	setApiId(ZB_EXPLICIT_TX_REQUEST);
+}
+
+ZBExplicitTxRequest::ZBExplicitTxRequest(XBeeAddress64 &addr64, uint16_t addr16, uint8_t broadcastRadius, uint8_t option, uint8_t *payload, uint8_t payloadLength, uint8_t frameId, uint8_t srcEndpoint, uint8_t dstEndpoint, uint16_t clusterId, uint16_t profileId)
+: ZBTxRequest(addr64, addr16, broadcastRadius, option, payload, payloadLength, frameId) {
+	_srcEndpoint = srcEndpoint;
+	_dstEndpoint = dstEndpoint;
+	_profileId = profileId;
+	_clusterId = clusterId;
+	setApiId(ZB_EXPLICIT_TX_REQUEST);
+}
+
+ZBExplicitTxRequest::ZBExplicitTxRequest(XBeeAddress64 &addr64, uint8_t *payload, uint8_t payloadLength)
+: ZBTxRequest(addr64, payload, payloadLength) {
+	_srcEndpoint = DEFAULT_ENDPOINT;
+	_dstEndpoint = DEFAULT_ENDPOINT;
+	_profileId = DEFAULT_PROFILE_ID;
+	_clusterId = DEFAULT_CLUSTER_ID;
+	setApiId(ZB_EXPLICIT_TX_REQUEST);
+}
+
+uint8_t ZBExplicitTxRequest::getFrameData(uint8_t pos) {
+	if (pos < 10) {
+		return ZBTxRequest::getFrameData(pos);
+	} else if (pos == 10) {
+		return _srcEndpoint;
+	} else if (pos == 11) {
+		return _dstEndpoint;
+	} else if (pos == 12) {
+		return (_clusterId >> 8) & 0xff;
+	} else if (pos == 13) {
+		return _clusterId & 0xff;
+	} else if (pos == 14) {
+		return (_profileId >> 8) & 0xff;
+	} else if (pos == 15) {
+		return _profileId & 0xff;
+	} else if (pos == 16) {
+		return _broadcastRadius;
+	} else if (pos == 17) {
+		return _option;
+	} else {
+		return getPayload()[pos - ZB_EXPLICIT_TX_API_LENGTH];
+	}
+}
+
+uint8_t ZBExplicitTxRequest::getFrameDataLength() {
+	return ZB_EXPLICIT_TX_API_LENGTH + getPayloadLength();
+}
+
+uint8_t ZBExplicitTxRequest::getSrcEndpoint() {
+	return _srcEndpoint;
+}
+
+uint8_t ZBExplicitTxRequest::getDstEndpoint() {
+	return _dstEndpoint;
+}
+
+uint16_t ZBExplicitTxRequest::getClusterId() {
+	return _clusterId;
+}
+
+uint16_t ZBExplicitTxRequest::getProfileId() {
+	return _profileId;
+}
+
+void ZBExplicitTxRequest::setSrcEndpoint(uint8_t endpoint) {
+	_srcEndpoint = endpoint;
+}
+
+void ZBExplicitTxRequest::setDstEndpoint(uint8_t endpoint) {
+	_dstEndpoint = endpoint;
+}
+
+void ZBExplicitTxRequest::setClusterId(uint16_t clusterId) {
+	_clusterId = clusterId;
+}
+
+void ZBExplicitTxRequest::setProfileId(uint16_t profileId) {
+	_profileId = profileId;
+}
+#endif
+
+#ifdef SERIES_1
+
+Tx16Request::Tx16Request() : PayloadRequest(TX_16_REQUEST, DEFAULT_FRAME_ID, NULL, 0) {
+	_option = ACK_OPTION;
+}
+
+Tx16Request::Tx16Request(uint16_t addr16, uint8_t option, uint8_t *data, uint8_t dataLength, uint8_t frameId) : PayloadRequest(TX_16_REQUEST, frameId, data, dataLength) {
+	_addr16 = addr16;
+	_option = option;
+}
+
+Tx16Request::Tx16Request(uint16_t addr16, uint8_t *data, uint8_t dataLength) : PayloadRequest(TX_16_REQUEST, DEFAULT_FRAME_ID, data, dataLength) {
+	_addr16 = addr16;
+	_option = ACK_OPTION;
+}
+
+uint8_t Tx16Request::getFrameData(uint8_t pos) {
+
+	if (pos == 0) {
+		return (_addr16 >> 8) & 0xff;
+	} else if (pos == 1) {
+		return _addr16 & 0xff;
+	} else if (pos == 2) {
+		return _option;
+	} else {
+		return getPayload()[pos - TX_16_API_LENGTH];
+	}
+}
+
+uint8_t Tx16Request::getFrameDataLength() {
+	return TX_16_API_LENGTH + getPayloadLength();
+}
+
+uint16_t Tx16Request::getAddress16() {
+	return _addr16;
+}
+
+void Tx16Request::setAddress16(uint16_t addr16) {
+	_addr16 = addr16;
+}
+
+uint8_t Tx16Request::getOption() {
+	return _option;
+}
+
+void Tx16Request::setOption(uint8_t option) {
+	_option = option;
+}
+
+Tx64Request::Tx64Request() : PayloadRequest(TX_64_REQUEST, DEFAULT_FRAME_ID, NULL, 0) {
+	_option = ACK_OPTION;
+}
+
+Tx64Request::Tx64Request(XBeeAddress64 &addr64, uint8_t option, uint8_t *data, uint8_t dataLength, uint8_t frameId) : PayloadRequest(TX_64_REQUEST, frameId, data, dataLength) {
+	_addr64 = addr64;
+	_option = option;
+}
+
+Tx64Request::Tx64Request(XBeeAddress64 &addr64, uint8_t *data, uint8_t dataLength) : PayloadRequest(TX_64_REQUEST, DEFAULT_FRAME_ID, data, dataLength) {
+	_addr64 = addr64;
+	_option = ACK_OPTION;
+}
+
+uint8_t Tx64Request::getFrameData(uint8_t pos) {
+
+	if (pos == 0) {
+		return (_addr64.getMsb() >> 24) & 0xff;
+	} else if (pos == 1) {
+		return (_addr64.getMsb() >> 16) & 0xff;
+	} else if (pos == 2) {
+		return (_addr64.getMsb() >> 8) & 0xff;
+	} else if (pos == 3) {
+		return _addr64.getMsb() & 0xff;
+	} else if (pos == 4) {
+		return (_addr64.getLsb() >> 24) & 0xff;
+	} else if (pos == 5) {
+		return (_addr64.getLsb() >> 16) & 0xff;
+	} else if (pos == 6) {
+		return(_addr64.getLsb() >> 8) & 0xff;
+	} else if (pos == 7) {
+		return _addr64.getLsb() & 0xff;
+	} else if (pos == 8) {
+		return _option;
+	} else {
+		return getPayload()[pos - TX_64_API_LENGTH];
+	}
+}
+
+uint8_t Tx64Request::getFrameDataLength() {
+	return TX_64_API_LENGTH + getPayloadLength();
+}
+
+XBeeAddress64& Tx64Request::getAddress64() {
+	return _addr64;
+}
+
+void Tx64Request::setAddress64(XBeeAddress64& addr64) {
+	_addr64 = addr64;
+}
+
+uint8_t Tx64Request::getOption() {
+	return _option;
+}
+
+void Tx64Request::setOption(uint8_t option) {
+	_option = option;
+}
+
+#endif
+
+AtCommandRequest::AtCommandRequest() : XBeeRequest(AT_COMMAND_REQUEST, DEFAULT_FRAME_ID) {
+	_command = NULL;
+	clearCommandValue();
+}
+
+AtCommandRequest::AtCommandRequest(uint8_t *command, uint8_t *commandValue, uint8_t commandValueLength) : XBeeRequest(AT_COMMAND_REQUEST, DEFAULT_FRAME_ID) {
+	_command = command;
+	_commandValue = commandValue;
+	_commandValueLength = commandValueLength;
+}
+
+AtCommandRequest::AtCommandRequest(uint8_t *command) : XBeeRequest(AT_COMMAND_REQUEST, DEFAULT_FRAME_ID) {
+	_command = command;
+	clearCommandValue();
+}
+
+uint8_t* AtCommandRequest::getCommand() {
+	return _command;
+}
+
+uint8_t* AtCommandRequest::getCommandValue() {
+	return _commandValue;
+}
+
+uint8_t AtCommandRequest::getCommandValueLength() {
+	return _commandValueLength;
+}
+
+void AtCommandRequest::setCommand(uint8_t* command) {
+	_command = command;
+}
+
+void AtCommandRequest::setCommandValue(uint8_t* value) {
+	_commandValue = value;
+}
+
+void AtCommandRequest::setCommandValueLength(uint8_t length) {
+	_commandValueLength = length;
+}
+
+uint8_t AtCommandRequest::getFrameData(uint8_t pos) {
+
+	if (pos == 0) {
+		return _command[0];
+	} else if (pos == 1) {
+		return _command[1];
+	} else {
+		return _commandValue[pos - AT_COMMAND_API_LENGTH];
+	}
+}
+
+void AtCommandRequest::clearCommandValue() {
+	_commandValue = NULL;
+	_commandValueLength = 0;
+}
+
+//void AtCommandRequest::reset() {
+//	 XBeeRequest::reset();
+//}
+
+uint8_t AtCommandRequest::getFrameDataLength() {
+	// command is 2 byte + length of value
+	return AT_COMMAND_API_LENGTH + _commandValueLength;
+}
+
+XBeeAddress64 RemoteAtCommandRequest::broadcastAddress64 = XBeeAddress64(0x0, BROADCAST_ADDRESS);
+
+RemoteAtCommandRequest::RemoteAtCommandRequest() : AtCommandRequest(NULL, NULL, 0) {
+	_remoteAddress16 = 0;
+	_applyChanges = false;
+	setApiId(REMOTE_AT_REQUEST);
+}
+
+RemoteAtCommandRequest::RemoteAtCommandRequest(uint16_t remoteAddress16, uint8_t *command, uint8_t *commandValue, uint8_t commandValueLength) : AtCommandRequest(command, commandValue, commandValueLength) {
+	_remoteAddress64 = broadcastAddress64;
+	_remoteAddress16 = remoteAddress16;
+	_applyChanges = true;
+	setApiId(REMOTE_AT_REQUEST);
+}
+
+RemoteAtCommandRequest::RemoteAtCommandRequest(uint16_t remoteAddress16, uint8_t *command) : AtCommandRequest(command, NULL, 0) {
+	_remoteAddress64 = broadcastAddress64;
+	_remoteAddress16 = remoteAddress16;
+	_applyChanges = false;
+	setApiId(REMOTE_AT_REQUEST);
+}
+
+RemoteAtCommandRequest::RemoteAtCommandRequest(XBeeAddress64 &remoteAddress64, uint8_t *command, uint8_t *commandValue, uint8_t commandValueLength) : AtCommandRequest(command, commandValue, commandValueLength) {
+	_remoteAddress64 = remoteAddress64;
+	// don't worry.. works for series 1 too!
+	_remoteAddress16 = ZB_BROADCAST_ADDRESS;
+	_applyChanges = true;
+	setApiId(REMOTE_AT_REQUEST);
+}
+
+RemoteAtCommandRequest::RemoteAtCommandRequest(XBeeAddress64 &remoteAddress64, uint8_t *command) : AtCommandRequest(command, NULL, 0) {
+	_remoteAddress64 = remoteAddress64;
+	_remoteAddress16 = ZB_BROADCAST_ADDRESS;
+	_applyChanges = false;
+	setApiId(REMOTE_AT_REQUEST);
+}
+
+uint16_t RemoteAtCommandRequest::getRemoteAddress16() {
+	return _remoteAddress16;
+}
+
+void RemoteAtCommandRequest::setRemoteAddress16(uint16_t remoteAddress16) {
+	_remoteAddress16 = remoteAddress16;
+}
+
+XBeeAddress64& RemoteAtCommandRequest::getRemoteAddress64() {
+	return _remoteAddress64;
+}
+
+void RemoteAtCommandRequest::setRemoteAddress64(XBeeAddress64 &remoteAddress64) {
+	_remoteAddress64 = remoteAddress64;
+}
+
+bool RemoteAtCommandRequest::getApplyChanges() {
+	return _applyChanges;
+}
+
+void RemoteAtCommandRequest::setApplyChanges(bool applyChanges) {
+	_applyChanges = applyChanges;
+}
+
+
+uint8_t RemoteAtCommandRequest::getFrameData(uint8_t pos) {
+	if (pos == 0) {
+		return (_remoteAddress64.getMsb() >> 24) & 0xff;
+	} else if (pos == 1) {
+		return (_remoteAddress64.getMsb() >> 16) & 0xff;
+	} else if (pos == 2) {
+		return (_remoteAddress64.getMsb() >> 8) & 0xff;
+	} else if (pos == 3) {
+		return _remoteAddress64.getMsb() & 0xff;
+	} else if (pos == 4) {
+		return (_remoteAddress64.getLsb() >> 24) & 0xff;
+	} else if (pos == 5) {
+		return (_remoteAddress64.getLsb() >> 16) & 0xff;
+	} else if (pos == 6) {
+		return(_remoteAddress64.getLsb() >> 8) & 0xff;
+	} else if (pos == 7) {
+		return _remoteAddress64.getLsb() & 0xff;
+	} else if (pos == 8) {
+		return (_remoteAddress16 >> 8) & 0xff;
+	} else if (pos == 9) {
+		return _remoteAddress16 & 0xff;
+	} else if (pos == 10) {
+		return _applyChanges ? 2: 0;
+	} else if (pos == 11) {
+		return getCommand()[0];
+	} else if (pos == 12) {
+		return getCommand()[1];
+	} else {
+		return getCommandValue()[pos - REMOTE_AT_COMMAND_API_LENGTH];
+	}
+}
+
+uint8_t RemoteAtCommandRequest::getFrameDataLength() {
+	return REMOTE_AT_COMMAND_API_LENGTH + getCommandValueLength();
+}
+
+
+// TODO
+//GenericRequest::GenericRequest(uint8_t* frame, uint8_t len, uint8_t apiId): XBeeRequest(apiId, *(frame), len) {
+//	_frame = frame;
+//}
+
+void XBee::send(XBeeRequest &request) {
+	// the new new deal
+
+	sendByte(START_BYTE, false);
+
+	// send length
+	uint8_t msbLen = ((request.getFrameDataLength() + 2) >> 8) & 0xff;
+	uint8_t lsbLen = (request.getFrameDataLength() + 2) & 0xff;
+
+	sendByte(msbLen, true);
+	sendByte(lsbLen, true);
+
+	// api id
+	sendByte(request.getApiId(), true);
+	sendByte(request.getFrameId(), true);
+
+	uint8_t checksum = 0;
+
+	// compute checksum, start at api id
+	checksum+= request.getApiId();
+	checksum+= request.getFrameId();
+
+	for (int i = 0; i < request.getFrameDataLength(); i++) {
+		sendByte(request.getFrameData(i), true);
+		checksum+= request.getFrameData(i);
+	}
+
+	// perform 2s complement
+	checksum = 0xff - checksum;
+
+	// send checksum
+	sendByte(checksum, true);
+}
+
+void XBee::sendByte(uint8_t b, bool escape) {
+
+	if (escape && (b == START_BYTE || b == ESCAPE || b == XON || b == XOFF)) {
+		write(ESCAPE);
+		write(b ^ 0x20);
+	} else {
+		write(b);
+	}
+}
+
+
+void XBeeWithCallbacks::loop() {
+	if (loopTop())
+		loopBottom();
+}
+
+bool XBeeWithCallbacks::loopTop() {
+	readPacket();
+	if (getResponse().isAvailable()) {
+		_onResponse.call(getResponse());
+		return true;
+	} else if (getResponse().isError()) {
+		_onPacketError.call(getResponse().getErrorCode());
+	}
+	return false;
+}
+
+void XBeeWithCallbacks::loopBottom() {
+	bool called = false;
+	uint8_t id = getResponse().getApiId();
+
+	if (id == ZB_TX_STATUS_RESPONSE) {
+		ZBTxStatusResponse response;
+		getResponse().getZBTxStatusResponse(response);
+		called = _onZBTxStatusResponse.call(response);
+	} else if (id == ZB_RX_RESPONSE) {
+		ZBRxResponse response;
+		getResponse().getZBRxResponse(response);
+		called = _onZBRxResponse.call(response);
+	} else if (id == ZB_EXPLICIT_RX_RESPONSE) {
+		ZBExplicitRxResponse response;
+		getResponse().getZBExplicitRxResponse(response);
+		called = _onZBExplicitRxResponse.call(response);
+	} else if (id == ZB_IO_SAMPLE_RESPONSE) {
+		ZBRxIoSampleResponse response;
+		getResponse().getZBRxIoSampleResponse(response);
+		called = _onZBRxIoSampleResponse.call(response);
+	} else if (id == TX_STATUS_RESPONSE) {
+		TxStatusResponse response;
+		getResponse().getTxStatusResponse(response);
+		called = _onTxStatusResponse.call(response);
+	} else if (id == RX_16_RESPONSE) {
+		Rx16Response response;
+		getResponse().getRx16Response(response);
+		called = _onRx16Response.call(response);
+	} else if (id == RX_64_RESPONSE) {
+		Rx64Response response;
+		getResponse().getRx64Response(response);
+		called = _onRx64Response.call(response);
+	} else if (id == RX_16_IO_RESPONSE) {
+		Rx16IoSampleResponse response;
+		getResponse().getRx16IoSampleResponse(response);
+		called = _onRx16IoSampleResponse.call(response);
+	} else if (id == RX_64_IO_RESPONSE) {
+		Rx64IoSampleResponse response;
+		getResponse().getRx64IoSampleResponse(response);
+		called = _onRx64IoSampleResponse.call(response);
+	} else if (id == MODEM_STATUS_RESPONSE) {
+		ModemStatusResponse response;
+		getResponse().getModemStatusResponse(response);
+		called = _onModemStatusResponse.call(response);
+	} else if (id == AT_COMMAND_RESPONSE) {
+		AtCommandResponse response;
+		getResponse().getAtCommandResponse(response);
+		called = _onAtCommandResponse.call(response);
+	} else if (id == REMOTE_AT_COMMAND_RESPONSE) {
+		RemoteAtCommandResponse response;
+		getResponse().getRemoteAtCommandResponse(response);
+		called = _onRemoteAtCommandResponse.call(response);
+	}
+
+	if (!called)
+		_onOtherResponse.call(getResponse());
+}
+
+uint8_t XBeeWithCallbacks::matchStatus(uint8_t frameId) {
+	uint8_t id = getResponse().getApiId();
+	uint8_t *data = getResponse().getFrameData();
+	uint8_t len = getResponse().getFrameDataLength();
+	uint8_t offset = 0;
+
+	// Figure out if this frame has a frameId and if so, where the
+	// status byte to return is located
+	if (id == AT_COMMAND_RESPONSE)
+		offset = 3;
+	else if (id == REMOTE_AT_COMMAND_RESPONSE)
+		offset = 13;
+	else if (id == TX_STATUS_RESPONSE)
+		offset = 1;
+	else if (id == ZB_TX_STATUS_RESPONSE)
+		offset = 4;
+
+	// If this is an API frame that contains a status, the frame is
+	// long enough to contain it and the frameId matches the one
+	// given, return the status byte
+	if (offset && offset < len && data[0] == frameId)
+		return data[offset];
+	return 0xff;
+}
+
+uint8_t XBeeWithCallbacks::waitForInternal(uint8_t apiId, void *response, uint16_t timeout, void *func, uintptr_t data, int16_t frameId) {
+    timer.reset();
+	timer.start();
+	do {
+		// Wait for a packet of the right type
+		if (loopTop()) {
+			if (frameId >= 0) {
+				uint8_t status = matchStatus(frameId);
+				// If a status was found, but it was not
+				// a zero success status, stop waiting
+				if (status != 0xff && status != 0)
+					return status;
+			}
+
+			if (getResponse().getApiId() == apiId) {
+				// If the type is right, call the right
+				// conversion function based on the
+				// ApiId and call the match function.
+				// Because the match function is
+				// essentially called in the same way,
+				// regardless of the subclass used, the
+				// compiler can reduce most of the below
+				// mess into a single piece of code
+				// (though for fully optimizing, the
+				// separate getXxxResponse() methods
+				// must be unified as well).
+				switch(apiId) {
+					case ZBTxStatusResponse::API_ID: {
+						ZBTxStatusResponse *r = (ZBTxStatusResponse*)response;
+						bool(*f)(ZBTxStatusResponse&,uintptr_t) = (bool(*)(ZBTxStatusResponse&,uintptr_t))func;
+						getResponse().getZBTxStatusResponse(*r);
+						if(!f || f(*r, data))
+							return 0;
+						break;
+					}
+					case ZBRxResponse::API_ID: {
+						ZBRxResponse *r = (ZBRxResponse*)response;
+						bool(*f)(ZBRxResponse&,uintptr_t) = (bool(*)(ZBRxResponse&,uintptr_t))func;
+						getResponse().getZBRxResponse(*r);
+						if(!f || f(*r, data))
+							return 0;
+						break;
+					}
+					case ZBExplicitRxResponse::API_ID: {
+						ZBExplicitRxResponse *r = (ZBExplicitRxResponse*)response;
+						bool(*f)(ZBExplicitRxResponse&,uintptr_t) = (bool(*)(ZBExplicitRxResponse&,uintptr_t))func;
+						getResponse().getZBExplicitRxResponse(*r);
+						if(!f || f(*r, data))
+							return 0;
+						break;
+					}
+					case ZBRxIoSampleResponse::API_ID: {
+						ZBRxIoSampleResponse *r = (ZBRxIoSampleResponse*)response;
+						bool(*f)(ZBRxIoSampleResponse&,uintptr_t) = (bool(*)(ZBRxIoSampleResponse&,uintptr_t))func;
+						getResponse().getZBRxIoSampleResponse(*r);
+						if(!f || f(*r, data))
+							return 0;
+						break;
+					}
+					case TxStatusResponse::API_ID: {
+						TxStatusResponse *r = (TxStatusResponse*)response;
+						bool(*f)(TxStatusResponse&,uintptr_t) = (bool(*)(TxStatusResponse&,uintptr_t))func;
+						getResponse().getTxStatusResponse(*r);
+						if(!f || f(*r, data))
+							return 0;
+						break;
+					}
+					case Rx16Response::API_ID: {
+						Rx16Response *r = (Rx16Response*)response;
+						bool(*f)(Rx16Response&,uintptr_t) = (bool(*)(Rx16Response&,uintptr_t))func;
+						getResponse().getRx16Response(*r);
+						if(!f || f(*r, data))
+							return 0;
+						break;
+					}
+					case Rx64Response::API_ID: {
+						Rx64Response *r = (Rx64Response*)response;
+						bool(*f)(Rx64Response&,uintptr_t) = (bool(*)(Rx64Response&,uintptr_t))func;
+						getResponse().getRx64Response(*r);
+						if(!f || f(*r, data))
+							return 0;
+						break;
+					}
+					case Rx16IoSampleResponse::API_ID: {
+						Rx16IoSampleResponse *r = (Rx16IoSampleResponse*)response;
+						bool(*f)(Rx16IoSampleResponse&,uintptr_t) = (bool(*)(Rx16IoSampleResponse&,uintptr_t))func;
+						getResponse().getRx16IoSampleResponse(*r);
+						if(!f || f(*r, data))
+							return 0;
+						break;
+					}
+					case Rx64IoSampleResponse::API_ID: {
+						Rx64IoSampleResponse *r = (Rx64IoSampleResponse*)response;
+						bool(*f)(Rx64IoSampleResponse&,uintptr_t) = (bool(*)(Rx64IoSampleResponse&,uintptr_t))func;
+						getResponse().getRx64IoSampleResponse(*r);
+						if(!f || f(*r, data))
+							return 0;
+						break;
+					}
+					case ModemStatusResponse::API_ID: {
+						ModemStatusResponse *r = (ModemStatusResponse*)response;
+						bool(*f)(ModemStatusResponse&,uintptr_t) = (bool(*)(ModemStatusResponse&,uintptr_t))func;
+						getResponse().getModemStatusResponse(*r);
+						if(!f || f(*r, data))
+							return 0;
+						break;
+					}
+					case AtCommandResponse::API_ID: {
+						AtCommandResponse *r = (AtCommandResponse*)response;
+						bool(*f)(AtCommandResponse&,uintptr_t) = (bool(*)(AtCommandResponse&,uintptr_t))func;
+						getResponse().getAtCommandResponse(*r);
+						if(!f || f(*r, data))
+							return 0;
+						break;
+					}
+					case RemoteAtCommandResponse::API_ID: {
+						RemoteAtCommandResponse *r = (RemoteAtCommandResponse*)response;
+						bool(*f)(RemoteAtCommandResponse&,uintptr_t) = (bool(*)(RemoteAtCommandResponse&,uintptr_t))func;
+						getResponse().getRemoteAtCommandResponse(*r);
+						if(!f || f(*r, data))
+							return 0;
+						break;
+					}
+				}
+			}
+			// Call regular callbacks
+			loopBottom();
+		}
+	} while (timer.read_ms() < timeout);
+	return XBEE_WAIT_TIMEOUT;
+}
+
+uint8_t XBeeWithCallbacks::waitForStatus(uint8_t frameId, uint16_t timeout) {
+	timer.reset();
+    timer.start();
+	do {
+		if (loopTop()) {
+			uint8_t status = matchStatus(frameId);
+			if (status != 0xff)
+				return status;
+
+			// Call regular callbacks
+			loopBottom();
+		}
+	} while (timer.read_ms() < timeout);
+	return XBEE_WAIT_TIMEOUT;
+}
+
+