From b227d0cd19b3244ab7c22ccdc02e145227e4f7f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Grome=C5=A1?= Date: Thu, 19 Jul 2018 16:21:47 +0200 Subject: [PATCH] XBee - Implemented API mode basics --- keywords.txt | 6 + src/TypeDef.h | 3 + src/modules/XBee.cpp | 283 ++++++++++++++++++++++++------------------- src/modules/XBee.h | 60 +++++---- 4 files changed, 204 insertions(+), 148 deletions(-) diff --git a/keywords.txt b/keywords.txt index b4abc4e1..6f5756d9 100644 --- a/keywords.txt +++ b/keywords.txt @@ -22,6 +22,7 @@ SX1277 KEYWORD1 SX1278 KEYWORD1 SX1279 KEYWORD1 XBee KEYWORD1 +XBeeSerial KEYWORD1 MQTTClient KEYWORD1 HTTPClient KEYWORD1 @@ -70,6 +71,8 @@ reset KEYWORD2 # XBee setDestinationAddress KEYWORD2 setPanId KEYWORD2 +getPacketSource KEYWORD2 +getPacketData KEYWORD2 # HTTP get KEYWORD2 @@ -136,3 +139,6 @@ MQTT_SUBS_SUCCESS_QOS_2 LITERAL1 ERR_MQTT_SUBS_FAILED LITERAL1 ERR_CMD_MODE_FAILED LITERAL1 +ERR_FRAME_MALFORMED LITERAL1 +ERR_FRAME_INCORRECT_CHECKSUM LITERAL1 +ERR_FRAME_UNEXPECTED_ID LITERAL1 diff --git a/src/TypeDef.h b/src/TypeDef.h index 06184745..d29f4b0b 100644 --- a/src/TypeDef.h +++ b/src/TypeDef.h @@ -94,5 +94,8 @@ // XBee status codes #define ERR_CMD_MODE_FAILED 0x02 +#define ERR_FRAME_MALFORMED 0x03 +#define ERR_FRAME_INCORRECT_CHECKSUM 0x04 +#define ERR_FRAME_UNEXPECTED_ID 0x05 #endif diff --git a/src/modules/XBee.cpp b/src/modules/XBee.cpp index 46096ab6..0228c322 100644 --- a/src/modules/XBee.cpp +++ b/src/modules/XBee.cpp @@ -1,23 +1,88 @@ #include "XBee.h" -XBeeApiFrame::XBeeApiFrame(uint8_t apiId, uint8_t frameId) { - _apiId = apiId; - _frameId = frameId; -} - -XBee::XBee(Module* module) { - _mod = module; +XBee::XBee(Module* mod) { + _mod = mod; + _frameID = 0x01; } uint8_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); + // wait for boot + delay(2000); + + // empty UART buffer (garbage data) + _mod->ATemptyBuffer(); + + // send test frame (get baudrate setting) + uint8_t frameID = _frameID++; + sendApiFrame(XBEE_API_FRAME_AT_COMMAND, frameID, "BD"); + delay(20); + + // get response code + return(readApiFrame(frameID, 4)); +} + +uint8_t XBee::transmit(uint8_t* dest, const char* payload, uint8_t radius) { + uint8_t destNetwork[] = {0xFF, 0xFE}; + return(transmit(dest, destNetwork, payload)); +} + +uint8_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; + delay(40); + + // get response code + return(readApiFrame(frameID, 5)); +} + +size_t XBee::available() { + +} + +String XBee::getPacketSource() { + +} + +String XBee::getPacketData() { + +} + +uint8_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, frameID, cmd, 10); + delay(40); + + // get response code + return(readApiFrame(frameID, 4)); +} + +XBeeSerial::XBeeSerial(Module* mod) : ISerial(mod) { + +} + +uint8_t XBeeSerial::begin(long speed) { // set module properties _mod->AtLineFeed = "\r"; _mod->baudrate = speed; @@ -43,13 +108,11 @@ uint8_t XBee::begin(long speed) { if(!_mod->ATsendCommand("ATCN")) { return(ERR_AT_FAILED); } - - delay(1000); return(ERR_NONE); } -uint8_t XBee::setDestinationAddress(const char* destinationAddressHigh, const char* destinationAddressLow) { +uint8_t XBeeSerial::setDestinationAddress(const char* destinationAddressHigh, const char* destinationAddressLow) { // enter command mode DEBUG_PRINTLN_STR("Entering command mode ..."); if(!enterCmdMode()) { @@ -87,7 +150,7 @@ uint8_t XBee::setDestinationAddress(const char* destinationAddressHigh, const ch return(ERR_NONE); } -uint8_t XBee::setPanId(const char* panId) { +uint8_t XBeeSerial::setPanId(const char* panId) { // enter command mode DEBUG_PRINTLN_STR("Entering command mode ..."); if(!enterCmdMode()) { @@ -114,7 +177,7 @@ uint8_t XBee::setPanId(const char* panId) { return(ERR_NONE); } -bool XBee::enterCmdMode() { +bool XBeeSerial::enterCmdMode() { for(uint8_t i = 0; i < 10; i++) { delay(1000); @@ -150,120 +213,92 @@ bool XBee::enterCmdMode() { } } -/*uint8_t XBee::transmit(uint32_t destinationAddressHigh, uint32_t destinationAddressLow, const char* data, uint8_t length) { - //build the API frame - uint8_t frameLength = length + 12; - uint8_t * frameData = new uint8_t[frameLength]; +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]; - //set the destination address - frameData[0] = (destinationAddressHigh >> 24) & 0xFF; - frameData[1] = (destinationAddressHigh >> 16) & 0xFF; - frameData[2] = (destinationAddressHigh >> 8) & 0xFF; - frameData[3] = destinationAddressHigh & 0xFF; + 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 - frameData[4] = (destinationAddressLow >> 24) & 0xFF; - frameData[5] = (destinationAddressLow >> 16) & 0xFF; - frameData[6] = (destinationAddressLow >> 8) & 0xFF; - frameData[7] = destinationAddressLow & 0xFF; + // calculate the checksum + uint8_t checksum = 0; + for(uint16_t i = 3; i < frameLength - 1; i++) { + checksum += frame[i]; + } + frame[5 + length] = 0xFF - checksum; - //set the destination network address - frameData[8] = 0xFF; - frameData[9] = 0xFE; - - //set broadcast radius (number of allowed hops, 0 - maximum) - frameData[10] = 0x00; - - //set the options - frameData[11] = 0x00; - - //copy payload data - for(uint8_t i = 0; i < length; i++) { - frameData[12 + i] = (uint8_t)data[i]; + // send the frame + for(uint16_t i = 0; i < frameLength; i++) { + _mod->ModuleSerial->write(frame[i]); } - //send the frame to XBee - sendApiFrame(XBEE_API_FRAME_ZIGBEE_TRANSMIT_REQUEST, frameData, frameLength); - - //deallocate memory - delete frameData; - - //wait for status frame - readApiFrame(1000); -}*/ + // deallocate memory + delete[] frame; +} -/*void XBee::sendApiFrame(uint8_t apiId, uint8_t* data, uint16_t length) { - //send frame start delimiter - _mod->ModuleSerial->write(XBEE_API_START); - - //send frame length (API ID, frame ID and data length) - write(((length + 2) >> 8) & 0xFF); - write((length + 2) & 0xFF); - - //send API ID - write(apiId); - - //send default frame ID (value 0x00 would disable some feedback) - write(XBEE_API_DEFAULT_FRAME_ID); - - //checksum is calculated from API ID, frame ID and data - uint8_t checksum = apiId; - checksum += XBEE_API_DEFAULT_FRAME_ID; - - //send the data and calculate checksum - for(uint16_t i = 0; i < length; i++) { - write(data[i]); - checksum += data[i]; +uint8_t XBee::readApiFrame(uint8_t frameID, uint8_t codePos) { + // get number of bytes in response + uint16_t numBytes = getNumBytes(10000, 5); + if(numBytes == 0) { + return(ERR_FRAME_MALFORMED); } - //send the checksum - checksum = 0xFF - checksum; - write(checksum); -}*/ + // checksum byte is not included in length field + numBytes++; + + // 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) { + return(ERR_FRAME_INCORRECT_CHECKSUM); + } + + // check frame ID + if(resp[1] != frameID) { + return(ERR_FRAME_UNEXPECTED_ID); + } + + uint8_t code = resp[codePos]; + delete[] resp; + return(code); +} -/*uint8_t XBee::readApiFrame(uint16_t timeout) { - //start the timer - unsigned long start = millis(); - - Serial.println("reading"); - - - //array to store frame length, type and ID - uint8_t header[4]; - while(millis() - start < timeout) { - Serial.println(_mod->ModuleSerial->available()); - //check buffer for new data - while(_mod->ModuleSerial->available()) { - uint8_t b = _mod->ModuleSerial->read(); - - Serial.write(b); - Serial.print('\t'); - Serial.println(b, HEX); - - if(b == XBEE_API_START) { - //received the start character - n = 0; - } else { - n++; - } - - //check escaped characters - if(b == XBEE_API_ESCAPE) { - //wait for the next byte - while(!_mod->ModuleSerial->available()); - - //resolve the escaped character - b =_mod->ModuleSerial->read(); - b = 0x20 ^ b; - } +uint16_t XBee::getNumBytes(uint32_t timeout, size_t minBytes) { + // wait for available data + uint32_t start = millis(); + while(_mod->ModuleSerial->available() < minBytes) { + if(millis() - start >= timeout) { + return(0); } } -}*/ - -/*void XBee::write(uint8_t b) { - if((b == XBEE_API_START) || (b == XBEE_API_ESCAPE) || (b == XBEE_API_XON) || (b == XBEE_API_XOFF)) { - _mod->ModuleSerial->write(XBEE_API_ESCAPE); - _mod->ModuleSerial->write(b ^ 0x20); - } else { - _mod->ModuleSerial->write(b); + + // 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]); +} diff --git a/src/modules/XBee.h b/src/modules/XBee.h index be1c89f0..e9c7b4d5 100644 --- a/src/modules/XBee.h +++ b/src/modules/XBee.h @@ -4,13 +4,13 @@ #include "ISerial.h" #include "TypeDef.h" -//API reserved characters +// API reserved characters #define XBEE_API_START 0x7E #define XBEE_API_ESCAPE 0x7D #define XBEE_API_XON 0x11 #define XBEE_API_XOFF 0x13 -//API frame IDs +// API frame IDs #define XBEE_API_FRAME_AT_COMMAND 0x08 #define XBEE_API_FRAME_AT_COMMAND_PARAMETER 0x09 #define XBEE_API_FRAME_ZIGBEE_TRANSMIT_REQUEST 0x10 @@ -31,36 +31,48 @@ #define XBEE_API_FRAME_ROUTE_RECORD 0xA1 #define XBEE_API_FRAME_MANY_TO_ONE_ROUTE_REQUEST 0xA3 -#define XBEE_API_DEFAULT_FRAME_ID 0x01 - -class XBeeApiFrame { +class XBeeSerial: public ISerial { public: - XBeeApiFrame(uint8_t apiId, uint8_t frameId); - - uint8_t getApiId(); - uint8_t getFrameId(); - - private: - uint16_t _length; - uint8_t _apiId; - uint8_t _frameId; - uint8_t* _data; -}; - -class XBee: public ISerial { - public: - XBee(Module* module); + // constructor + XBeeSerial(Module* mod); + // basic methods uint8_t begin(long speed); + // configuration methods uint8_t setDestinationAddress(const char* destinationAddressHigh, const char* destinationAddressLow); - uint8_t setPanId(const char* panId); + uint8_t setPanId(const char* panID); private: bool enterCmdMode(); - //void sendApiFrame(uint8_t id, uint8_t* data, uint16_t length); - //uint8_t readApiFrame(uint16_t timeout); - //void write(uint8_t b); + +}; + +class XBee { + public: + // constructor + XBee(Module* mod); + + // basic methods + uint8_t begin(long speed); + uint8_t transmit(uint8_t* dest, const char* payload, uint8_t radius = 1); + uint8_t transmit(uint8_t* dest, uint8_t* destNetwork, const char* payload, uint8_t radius = 1); + size_t available(); + String getPacketSource(); + String getPacketData(); + + // configuration methods + uint8_t setPanId(uint8_t* panID); + + private: + Module* _mod; + uint8_t _frameID; + + void sendApiFrame(uint8_t type, uint8_t id, const char* data); + void sendApiFrame(uint8_t type, uint8_t id, uint8_t* data, uint16_t length); + uint8_t readApiFrame(uint8_t frameID, uint8_t codePos); + + uint16_t getNumBytes(uint32_t timeout = 10000, size_t minBytes = 10); }; #endif