RadioLibSmol/src/modules/XBee/XBee.cpp
2020-04-01 14:01:57 +02:00

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]);
}