483 lines
12 KiB
C++
483 lines
12 KiB
C++
#include "XBee.h"
|
|
|
|
XBee::XBee(Module* mod) {
|
|
_mod = mod;
|
|
_frameID = 0x01;
|
|
_frameLength = 0;
|
|
_frameHeaderProcessed = false;
|
|
}
|
|
|
|
int16_t XBee::begin(long speed) {
|
|
// set module properties
|
|
_mod->baudrate = speed;
|
|
_mod->init(RADIOLIB_USE_UART);
|
|
|
|
// reset module
|
|
reset();
|
|
|
|
// empty UART buffer (garbage data)
|
|
_mod->ATemptyBuffer();
|
|
|
|
// try to find the XBee
|
|
bool flagFound = false;
|
|
uint8_t i = 0;
|
|
while((i < 10) && !flagFound) {
|
|
// hardware reset should return 2 modem status frames - 1st status 0x00, second status 0x06
|
|
int16_t state = readApiFrame(0x00, 1, 2000);
|
|
readApiFrame(0x00, 1, 2000);
|
|
|
|
if(state == ERR_NONE) {
|
|
flagFound = true;
|
|
} else {
|
|
RADIOLIB_DEBUG_PRINT(F("XBee not found! ("));
|
|
RADIOLIB_DEBUG_PRINT(i + 1);
|
|
RADIOLIB_DEBUG_PRINT(F(" of 10 tries) STATE == "));
|
|
RADIOLIB_DEBUG_PRINTLN(state);
|
|
RADIOLIB_DEBUG_PRINTLN(F("Resetting ..."));
|
|
reset();
|
|
delay(1000);
|
|
_mod->ATemptyBuffer();
|
|
i++;
|
|
}
|
|
}
|
|
|
|
if(!flagFound) {
|
|
RADIOLIB_DEBUG_PRINTLN(F("No XBee found!"));
|
|
return(ERR_CMD_MODE_FAILED);
|
|
} else {
|
|
RADIOLIB_DEBUG_PRINTLN(F("Found XBee!"));
|
|
}
|
|
|
|
return(ERR_NONE);
|
|
}
|
|
|
|
void XBee::reset() {
|
|
pinMode(_mod->getRst(), OUTPUT);
|
|
digitalWrite(_mod->getRst(), LOW);
|
|
delay(1);
|
|
digitalWrite(_mod->getRst(), HIGH);
|
|
}
|
|
|
|
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;
|
|
#ifdef RADIOLIB_STATIC_ONLY
|
|
uint8_t cmd[RADIOLIB_STATIC_ARRAY_SIZE];
|
|
#else
|
|
uint8_t* cmd = new uint8_t[dataLen];
|
|
#endif
|
|
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);
|
|
#ifndef RADIOLIB_STATIC_ONLY
|
|
delete[] cmd;
|
|
#endif
|
|
|
|
// 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);
|
|
}
|
|
|
|
#ifdef RADIOLIB_STATIC_ONLY
|
|
char frame[RADIOLIB_STATIC_ARRAY_SIZE];
|
|
#else
|
|
uint8_t* frame = new uint8_t[_frameLength];
|
|
#endif
|
|
for(size_t i = 0; i < _frameLength; i++) {
|
|
frame[i] = _mod->ModuleSerial->read();
|
|
}
|
|
|
|
// save packet source and data
|
|
size_t payloadLength = _frameLength - 12;
|
|
#ifndef RADIOLIB_STATIC_ONLY
|
|
delete[] _packetData;
|
|
_packetData = new char[payloadLength];
|
|
#endif
|
|
memcpy(_packetData, frame + 12, payloadLength - 1);
|
|
_packetData[payloadLength - 1] = '\0';
|
|
memcpy(_packetSource, frame + 1, 8);
|
|
|
|
#ifndef RADIOLIB_STATIC_ONLY
|
|
delete[] frame;
|
|
#endif
|
|
_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);
|
|
RADIOLIB_ASSERT(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(RADIOLIB_USE_UART);
|
|
|
|
// reset module
|
|
reset();
|
|
|
|
// empty UART buffer (garbage data)
|
|
_mod->ATemptyBuffer();
|
|
|
|
// enter command mode
|
|
RADIOLIB_DEBUG_PRINTLN(F("Entering command mode ..."));
|
|
if(!enterCmdMode()) {
|
|
return(ERR_CMD_MODE_FAILED);
|
|
}
|
|
|
|
// test AT setup
|
|
RADIOLIB_DEBUG_PRINTLN(F("Sending test command ..."));
|
|
if(!_mod->ATsendCommand("AT")) {
|
|
return(ERR_AT_FAILED);
|
|
}
|
|
|
|
// exit command mode
|
|
RADIOLIB_DEBUG_PRINTLN(F("Exiting command mode ..."));
|
|
if(!_mod->ATsendCommand("ATCN")) {
|
|
return(ERR_AT_FAILED);
|
|
}
|
|
|
|
return(ERR_NONE);
|
|
}
|
|
|
|
void XBeeSerial::reset() {
|
|
pinMode(_mod->getRst(), OUTPUT);
|
|
digitalWrite(_mod->getRst(), LOW);
|
|
delay(1);
|
|
digitalWrite(_mod->getRst(), HIGH);
|
|
pinMode(_mod->getRst(), INPUT);
|
|
}
|
|
|
|
int16_t XBeeSerial::setDestinationAddress(const char* destinationAddressHigh, const char* destinationAddressLow) {
|
|
// enter command mode
|
|
RADIOLIB_DEBUG_PRINTLN(F("Entering command mode ..."));
|
|
if(!enterCmdMode()) {
|
|
return(ERR_CMD_MODE_FAILED);
|
|
}
|
|
|
|
// set higher address bytes
|
|
RADIOLIB_DEBUG_PRINTLN(F("Setting address (high) ..."));
|
|
#ifdef RADIOLIB_STATIC_ONLY
|
|
char addressHigh[13];
|
|
#else
|
|
char* addressHigh = new char[strlen(destinationAddressHigh) + 4];
|
|
#endif
|
|
strcpy(addressHigh, "ATDH");
|
|
strcat(addressHigh, destinationAddressHigh);
|
|
bool res = _mod->ATsendCommand(addressHigh);
|
|
#ifndef RADIOLIB_STATIC_ONLY
|
|
delete[] addressHigh;
|
|
#endif
|
|
if(!res) {
|
|
return(ERR_AT_FAILED);
|
|
}
|
|
|
|
// set lower address bytes
|
|
RADIOLIB_DEBUG_PRINTLN(F("Setting address (low) ..."));
|
|
#ifdef RADIOLIB_STATIC_ONLY
|
|
char addressLow[13];
|
|
#else
|
|
char* addressLow = new char[strlen(destinationAddressLow) + 4];
|
|
#endif
|
|
strcpy(addressLow, "ATDL");
|
|
strcat(addressLow, destinationAddressLow);
|
|
res = _mod->ATsendCommand(addressLow);
|
|
#ifndef RADIOLIB_STATIC_ONLY
|
|
delete[] addressLow;
|
|
#endif
|
|
if(!res) {
|
|
return(ERR_AT_FAILED);
|
|
}
|
|
|
|
// exit command mode
|
|
RADIOLIB_DEBUG_PRINTLN(F("Exiting command mode ..."));
|
|
if(!_mod->ATsendCommand("ATCN")) {
|
|
return(ERR_AT_FAILED);
|
|
}
|
|
|
|
return(ERR_NONE);
|
|
}
|
|
|
|
int16_t XBeeSerial::setPanId(const char* panId) {
|
|
// enter command mode
|
|
RADIOLIB_DEBUG_PRINTLN(F("Entering command mode ..."));
|
|
if(!enterCmdMode()) {
|
|
return(ERR_CMD_MODE_FAILED);
|
|
}
|
|
|
|
// set PAN ID
|
|
RADIOLIB_DEBUG_PRINTLN(F("Setting PAN ID ..."));
|
|
#ifdef RADIOLIB_STATIC_ONLY
|
|
char cmd[21];
|
|
#else
|
|
char* cmd = new char[strlen(panId) + 4];
|
|
#endif
|
|
strcpy(cmd, "ATID");
|
|
strcat(cmd, panId);
|
|
bool res = _mod->ATsendCommand(cmd);
|
|
#ifndef RADIOLIB_STATIC_ONLY
|
|
delete[] cmd;
|
|
#endif
|
|
if(!res) {
|
|
return(ERR_AT_FAILED);
|
|
}
|
|
|
|
// exit command mode
|
|
RADIOLIB_DEBUG_PRINTLN(F("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 {
|
|
RADIOLIB_DEBUG_PRINT(F("Unable to enter command mode! ("));
|
|
RADIOLIB_DEBUG_PRINT(i + 1);
|
|
RADIOLIB_DEBUG_PRINTLN(F(" of 10 tries)"));
|
|
|
|
reset();
|
|
|
|
_mod->ATsendCommand("ATCN");
|
|
}
|
|
}
|
|
|
|
RADIOLIB_DEBUG_PRINTLN(F("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);
|
|
RADIOLIB_ASSERT(state);
|
|
|
|
// apply changes
|
|
frameID = _frameID++;
|
|
sendApiFrame(XBEE_API_FRAME_AT_COMMAND_QUEUE, frameID, "AC");
|
|
|
|
// get response code
|
|
state = readApiFrame(frameID, 4);
|
|
|
|
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;
|
|
#ifdef RADIOLIB_STATIC_ONLY
|
|
uint8_t frame[RADIOLIB_STATIC_ARRAY_SIZE];
|
|
#else
|
|
uint8_t* frame = new uint8_t[frameLength];
|
|
#endif
|
|
|
|
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
|
|
#ifndef RADIOLIB_STATIC_ONLY
|
|
delete[] frame;
|
|
#endif
|
|
}
|
|
|
|
int16_t XBee::readApiFrame(uint8_t frameID, uint8_t codePos, uint16_t timeout) {
|
|
// 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(timeout/2, 3);
|
|
if(numBytes == 0) {
|
|
return(ERR_FRAME_NO_RESPONSE);
|
|
}
|
|
|
|
// 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() < (int16_t)numBytes) {
|
|
yield();
|
|
if(millis() - start >= timeout/2) {
|
|
return(ERR_FRAME_MALFORMED);
|
|
}
|
|
}
|
|
RADIOLIB_DEBUG_PRINT(F("frame data field length: "));
|
|
RADIOLIB_DEBUG_PRINTLN(numBytes);
|
|
|
|
// read the response
|
|
#ifdef RADIOLIB_STATIC_ONLY
|
|
uint8_t resp[RADIOLIB_STATIC_ARRAY_SIZE];
|
|
#else
|
|
uint8_t* resp = new uint8_t[numBytes];
|
|
#endif
|
|
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++) {
|
|
RADIOLIB_DEBUG_PRINT(resp[i], HEX);
|
|
RADIOLIB_DEBUG_PRINT('\t');
|
|
checksum += resp[i];
|
|
}
|
|
RADIOLIB_DEBUG_PRINTLN();
|
|
if(checksum != 0xFF) {
|
|
RADIOLIB_DEBUG_PRINTLN(checksum, HEX);
|
|
return(ERR_FRAME_INCORRECT_CHECKSUM);
|
|
}
|
|
|
|
// check frame ID
|
|
if(resp[1] != frameID) {
|
|
RADIOLIB_DEBUG_PRINT(F("received frame ID: "));
|
|
RADIOLIB_DEBUG_PRINTLN(resp[1]);
|
|
RADIOLIB_DEBUG_PRINT(F("expected frame ID: "));
|
|
RADIOLIB_DEBUG_PRINTLN(frameID);
|
|
return(ERR_FRAME_UNEXPECTED_ID);
|
|
}
|
|
|
|
// codePos does not include start delimiter and frame ID
|
|
uint8_t code = resp[codePos];
|
|
#ifndef RADIOLIB_STATIC_ONLY
|
|
delete[] resp;
|
|
#endif
|
|
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) {
|
|
yield();
|
|
if(millis() - start >= timeout) {
|
|
return(0);
|
|
}
|
|
}
|
|
|
|
// read response
|
|
uint8_t resp[3];
|
|
uint8_t i = 0;
|
|
RADIOLIB_DEBUG_PRINT(F("reading frame length: "));
|
|
while(_mod->ModuleSerial->available() > 0) {
|
|
yield();
|
|
uint8_t b = _mod->ModuleSerial->read();
|
|
RADIOLIB_DEBUG_PRINT(b, HEX);
|
|
RADIOLIB_DEBUG_PRINT('\t');
|
|
resp[i++] = b;
|
|
if(i == 3) {
|
|
break;
|
|
}
|
|
}
|
|
RADIOLIB_DEBUG_PRINTLN();
|
|
|
|
return((resp[1] << 8) | resp[2]);
|
|
}
|