RadioLibSmol/src/modules/XBee.cpp
2018-09-23 18:56:53 +02:00

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