424 lines
10 KiB
C++
424 lines
10 KiB
C++
#include "XBee.h"
|
|
|
|
XBee::XBee(Module* mod) {
|
|
_mod = mod;
|
|
_frameID = 0x01;
|
|
_frameLength = 0;
|
|
_frameHeaderProcessed = false;
|
|
_packetData = new char[0];
|
|
}
|
|
|
|
int16_t XBee::begin(long speed) {
|
|
// set Arduino pins
|
|
/*pinMode(A4, OUTPUT);
|
|
pinMode(A5, OUTPUT);
|
|
pinMode(3, OUTPUT);
|
|
digitalWrite(A4, LOW);
|
|
digitalWrite(A5, LOW);
|
|
digitalWrite(3, HIGH);*/
|
|
|
|
// set module properties
|
|
_mod->baudrate = speed;
|
|
_mod->init(USE_UART, INT_NONE);
|
|
|
|
// reset module
|
|
reset();
|
|
|
|
// empty UART buffer (garbage data)
|
|
_mod->ATemptyBuffer();
|
|
|
|
// send test frame (get baudrate setting)
|
|
uint8_t frameID = _frameID++;
|
|
sendApiFrame(XBEE_API_FRAME_AT_COMMAND_QUEUE, frameID, "BD");
|
|
|
|
// get response code
|
|
int16_t state = readApiFrame(frameID, 4);
|
|
if(state != ERR_NONE) {
|
|
return(state);
|
|
}
|
|
|
|
return(state);
|
|
}
|
|
|
|
void XBee::reset() {
|
|
pinMode(_mod->int1(), OUTPUT);
|
|
delay(10);
|
|
digitalWrite(_mod->int1(), HIGH);
|
|
delay(500);
|
|
digitalWrite(_mod->int1(), LOW);
|
|
delay(500);
|
|
pinMode(_mod->int1(), INPUT);
|
|
delay(500);
|
|
}
|
|
|
|
int16_t XBee::transmit(uint8_t* dest, const char* payload, uint8_t radius) {
|
|
uint8_t destNetwork[] = {0xFF, 0xFE};
|
|
return(transmit(dest, destNetwork, payload, radius));
|
|
}
|
|
|
|
int16_t XBee::transmit(uint8_t* dest, uint8_t* destNetwork, const char* payload, uint8_t radius) {
|
|
// build the frame
|
|
size_t payloadLen = strlen(payload);
|
|
size_t dataLen = 8 + 2 + 1 + 1 + payloadLen;
|
|
uint8_t* cmd = new uint8_t[dataLen];
|
|
memcpy(cmd, dest, 8);
|
|
memcpy(cmd + 8, destNetwork, 2);
|
|
cmd[10] = radius;
|
|
cmd[11] = 0x01; // options: no retries
|
|
memcpy(cmd + 12, payload, payloadLen);
|
|
|
|
// send frame
|
|
uint8_t frameID = _frameID++;
|
|
sendApiFrame(XBEE_API_FRAME_ZIGBEE_TRANSMIT_REQUEST, frameID, cmd, dataLen);
|
|
delete[] cmd;
|
|
|
|
// get response code
|
|
return(readApiFrame(frameID, 5));
|
|
}
|
|
|
|
size_t XBee::available() {
|
|
// check if there are data available in the buffer
|
|
size_t serialBytes = _mod->ModuleSerial->available();
|
|
if(serialBytes < 3) {
|
|
return(0);
|
|
}
|
|
|
|
uint8_t header[3];
|
|
if(!_frameHeaderProcessed) {
|
|
// read frame header
|
|
for(uint8_t i = 0; i < 3; i++) {
|
|
header[i] = _mod->ModuleSerial->read();
|
|
}
|
|
|
|
// check if we received API frame
|
|
if(header[0] != XBEE_API_START) {
|
|
return(0);
|
|
}
|
|
|
|
// get expected frame length
|
|
_frameLength = ((header[1] << 8) | header[2]) + 1;
|
|
_frameHeaderProcessed = true;
|
|
}
|
|
|
|
// check if the header is complete
|
|
if(serialBytes < _frameLength) {
|
|
return(0);
|
|
}
|
|
|
|
uint8_t* frame = new uint8_t[_frameLength]; //24
|
|
for(size_t i = 0; i < _frameLength; i++) {
|
|
frame[i] = _mod->ModuleSerial->read();
|
|
}
|
|
|
|
// save packet source and data
|
|
size_t payloadLength = _frameLength - 12;
|
|
delete[] _packetData;
|
|
_packetData = new char[payloadLength];
|
|
memcpy(_packetData, frame + 12, payloadLength - 1);
|
|
_packetData[payloadLength - 1] = '\0';
|
|
memcpy(_packetSource, frame + 1, 8);
|
|
|
|
delete[] frame;
|
|
_frameLength = 0;
|
|
_frameHeaderProcessed = false;
|
|
|
|
// return number of bytes in payload
|
|
return(payloadLength);
|
|
}
|
|
|
|
String XBee::getPacketSource() {
|
|
char buff[17];
|
|
sprintf(buff, "%02X%02X%02X%02X%02X%02X%02X%02X", _packetSource[0], _packetSource[1], _packetSource[2], _packetSource[3],
|
|
_packetSource[4], _packetSource[5], _packetSource[6], _packetSource[7]);
|
|
String str(buff);
|
|
return(str);
|
|
}
|
|
|
|
String XBee::getPacketData() {
|
|
String str(_packetData);
|
|
return(str);
|
|
}
|
|
|
|
int16_t XBee::setPanId(uint8_t* panId) {
|
|
// build AT command
|
|
uint8_t cmd[10];
|
|
memcpy(cmd, "ID", 2);
|
|
memcpy(cmd + 2, panId, 8);
|
|
|
|
// send frame
|
|
uint8_t frameID = _frameID++;
|
|
sendApiFrame(XBEE_API_FRAME_AT_COMMAND_QUEUE, frameID, cmd, 10);
|
|
|
|
// get response code
|
|
int16_t state = readApiFrame(frameID, 4);
|
|
if(state != ERR_NONE) {
|
|
return(state);
|
|
}
|
|
|
|
// confirm changes
|
|
return(confirmChanges());
|
|
}
|
|
|
|
XBeeSerial::XBeeSerial(Module* mod) : ISerial(mod) {
|
|
|
|
}
|
|
|
|
int16_t XBeeSerial::begin(long speed) {
|
|
// set module properties
|
|
_mod->AtLineFeed = "\r";
|
|
_mod->baudrate = speed;
|
|
_mod->init(USE_UART, INT_NONE);
|
|
|
|
// empty UART buffer (garbage data)
|
|
_mod->ATemptyBuffer();
|
|
|
|
// enter command mode
|
|
DEBUG_PRINTLN_STR("Entering command mode ...");
|
|
if(!enterCmdMode()) {
|
|
return(ERR_CMD_MODE_FAILED);
|
|
}
|
|
|
|
// test AT setup
|
|
DEBUG_PRINTLN_STR("Sending test command ...");
|
|
if(!_mod->ATsendCommand("AT")) {
|
|
return(ERR_AT_FAILED);
|
|
}
|
|
|
|
// exit command mode
|
|
DEBUG_PRINTLN_STR("Exiting command mode ...");
|
|
if(!_mod->ATsendCommand("ATCN")) {
|
|
return(ERR_AT_FAILED);
|
|
}
|
|
|
|
return(ERR_NONE);
|
|
}
|
|
|
|
void XBeeSerial::reset() {
|
|
pinMode(_mod->int1(), OUTPUT);
|
|
delay(10);
|
|
digitalWrite(_mod->int1(), HIGH);
|
|
delay(500);
|
|
digitalWrite(_mod->int1(), LOW);
|
|
delay(500);
|
|
pinMode(_mod->int1(), INPUT);
|
|
delay(500);
|
|
}
|
|
|
|
int16_t XBeeSerial::setDestinationAddress(const char* destinationAddressHigh, const char* destinationAddressLow) {
|
|
// enter command mode
|
|
DEBUG_PRINTLN_STR("Entering command mode ...");
|
|
if(!enterCmdMode()) {
|
|
return(ERR_CMD_MODE_FAILED);
|
|
}
|
|
|
|
// set higher address bytes
|
|
DEBUG_PRINTLN_STR("Setting address (high) ...");
|
|
char* addressHigh = new char[strlen(destinationAddressHigh) + 4];
|
|
strcpy(addressHigh, "ATDH");
|
|
strcat(addressHigh, destinationAddressHigh);
|
|
bool res = _mod->ATsendCommand(addressHigh);
|
|
delete[] addressHigh;
|
|
if(!res) {
|
|
return(ERR_AT_FAILED);
|
|
}
|
|
|
|
// set lower address bytes
|
|
DEBUG_PRINTLN_STR("Setting address (low) ...");
|
|
char* addressLow = new char[strlen(destinationAddressLow) + 4];
|
|
strcpy(addressLow, "ATDL");
|
|
strcat(addressLow, destinationAddressLow);
|
|
res = _mod->ATsendCommand(addressLow);
|
|
delete[] addressLow;
|
|
if(!res) {
|
|
return(ERR_AT_FAILED);
|
|
}
|
|
|
|
// exit command mode
|
|
DEBUG_PRINTLN_STR("Exiting command mode ...");
|
|
if(!_mod->ATsendCommand("ATCN")) {
|
|
return(ERR_AT_FAILED);
|
|
}
|
|
|
|
return(ERR_NONE);
|
|
}
|
|
|
|
int16_t XBeeSerial::setPanId(const char* panId) {
|
|
// enter command mode
|
|
DEBUG_PRINTLN_STR("Entering command mode ...");
|
|
if(!enterCmdMode()) {
|
|
return(ERR_CMD_MODE_FAILED);
|
|
}
|
|
|
|
// set PAN ID
|
|
DEBUG_PRINTLN_STR("Setting PAN ID ...");
|
|
char* cmd = new char[strlen(panId) + 4];
|
|
strcpy(cmd, "ATID");
|
|
strcat(cmd, panId);
|
|
bool res = _mod->ATsendCommand(cmd);
|
|
delete[] cmd;
|
|
if(!res) {
|
|
return(ERR_AT_FAILED);
|
|
}
|
|
|
|
// exit command mode
|
|
DEBUG_PRINTLN_STR("Exiting command mode ...");
|
|
if(!_mod->ATsendCommand("ATCN")) {
|
|
return(ERR_AT_FAILED);
|
|
}
|
|
|
|
return(ERR_NONE);
|
|
}
|
|
|
|
bool XBeeSerial::enterCmdMode() {
|
|
for(uint8_t i = 0; i < 10; i++) {
|
|
delay(1000);
|
|
|
|
_mod->ModuleSerial->write('+');
|
|
_mod->ModuleSerial->write('+');
|
|
_mod->ModuleSerial->write('+');
|
|
|
|
delay(1000);
|
|
|
|
if(_mod->ATgetResponse()) {
|
|
return(true);
|
|
} else {
|
|
DEBUG_PRINT_STR("Unable to enter command mode! (");
|
|
DEBUG_PRINT(i + 1);
|
|
DEBUG_PRINTLN_STR(" of 10 tries)");
|
|
|
|
reset();
|
|
|
|
_mod->ATsendCommand("ATCN");
|
|
}
|
|
}
|
|
|
|
DEBUG_PRINTLN_STR("Terminated, check your wiring. Is AT FW uploaded?");
|
|
return(false);
|
|
}
|
|
|
|
int16_t XBee::confirmChanges() {
|
|
// save changes to non-volatile memory
|
|
uint8_t frameID = _frameID++;
|
|
sendApiFrame(XBEE_API_FRAME_AT_COMMAND_QUEUE, frameID, "WR");
|
|
|
|
// get response code
|
|
int16_t state = readApiFrame(frameID, 4);
|
|
if(state != ERR_NONE) {
|
|
return(state);
|
|
}
|
|
|
|
// apply changes
|
|
frameID = _frameID++;
|
|
sendApiFrame(XBEE_API_FRAME_AT_COMMAND_QUEUE, frameID, "AC");
|
|
|
|
// get response code
|
|
state = readApiFrame(frameID, 4);
|
|
if(state != ERR_NONE) {
|
|
return(state);
|
|
}
|
|
|
|
return(state);
|
|
}
|
|
|
|
void XBee::sendApiFrame(uint8_t type, uint8_t id, const char* data) {
|
|
sendApiFrame(type, id, (uint8_t*)data, strlen(data));
|
|
}
|
|
|
|
void XBee::sendApiFrame(uint8_t type, uint8_t id, uint8_t* data, uint16_t length) {
|
|
// build the API frame
|
|
size_t frameLength = 1 + 2 + length + 1 + 2;
|
|
uint8_t* frame = new uint8_t[frameLength];
|
|
|
|
frame[0] = 0x7E; // start delimiter
|
|
frame[1] = ((length + 2) & 0xFF00) >> 8; // length MSB
|
|
frame[2] = (length + 2) & 0x00FF; // length LSB
|
|
frame[3] = type; // frame type
|
|
frame[4] = id; // frame ID
|
|
memcpy(frame + 5, data, length); // data
|
|
|
|
// calculate the checksum
|
|
uint8_t checksum = 0;
|
|
for(uint16_t i = 3; i < frameLength - 1; i++) {
|
|
checksum += frame[i];
|
|
}
|
|
frame[5 + length] = 0xFF - checksum;
|
|
|
|
// send the frame
|
|
for(uint16_t i = 0; i < frameLength; i++) {
|
|
_mod->ModuleSerial->write(frame[i]);
|
|
}
|
|
|
|
// deallocate memory
|
|
delete[] frame;
|
|
}
|
|
|
|
int16_t XBee::readApiFrame(uint8_t frameID, uint8_t codePos) {
|
|
// TODO: modemStatus frames may be sent at any time, interfering with frame parsing. Add check to make sure this does not happen.
|
|
|
|
// get number of bytes in response (must be enough to read the length field
|
|
uint16_t numBytes = getNumBytes(5000, 3);
|
|
if(numBytes == 0) {
|
|
return(ERR_FRAME_MALFORMED);
|
|
}
|
|
|
|
// checksum byte is not included in length field
|
|
numBytes++;
|
|
|
|
// wait until all response bytes are available (5s timeout)
|
|
uint32_t start = millis();
|
|
while(_mod->ModuleSerial->available() < numBytes) {
|
|
if(millis() - start >= 5000) {
|
|
return(ERR_FRAME_MALFORMED);
|
|
}
|
|
}
|
|
|
|
// read the response
|
|
uint8_t* resp = new uint8_t[numBytes];
|
|
for(uint16_t i = 0; i < numBytes; i++) {
|
|
resp[i] = _mod->ModuleSerial->read();
|
|
}
|
|
|
|
// verify checksum
|
|
uint8_t checksum = 0;
|
|
for(uint16_t i = 0; i < numBytes; i++) {
|
|
checksum += resp[i];
|
|
}
|
|
if(checksum != 0xFF) {
|
|
DEBUG_PRINTLN_HEX(checksum);
|
|
return(ERR_FRAME_INCORRECT_CHECKSUM);
|
|
}
|
|
|
|
// check frame ID
|
|
if(resp[1] != frameID) {
|
|
DEBUG_PRINTLN(resp[1]);
|
|
return(ERR_FRAME_UNEXPECTED_ID);
|
|
}
|
|
|
|
// codePos does not include start delimiter and frame ID
|
|
uint8_t code = resp[codePos];
|
|
delete[] resp;
|
|
return(code);
|
|
}
|
|
|
|
uint16_t XBee::getNumBytes(uint32_t timeout, size_t minBytes) {
|
|
// wait for available data
|
|
uint32_t start = millis();
|
|
while((size_t)_mod->ModuleSerial->available() < minBytes) {
|
|
if(millis() - start >= timeout) {
|
|
return(0);
|
|
}
|
|
}
|
|
|
|
// read response
|
|
uint8_t resp[3];
|
|
uint8_t i = 0;
|
|
while(_mod->ModuleSerial->available() > 0) {
|
|
uint8_t b = _mod->ModuleSerial->read();
|
|
resp[i++] = b;
|
|
if(i == 3) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
return((resp[1] << 8) | resp[2]);
|
|
}
|