Solutions for the XBee experiments for LPC812 MAX

Dependencies:   mbed

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers XBee.cpp Source File

XBee.cpp

00001 
00002 #include "mbed.h"
00003 #include "mbed_debug.h"
00004 #include "XBee.h"
00005 
00006 #define XBEE_END_CMD ("\r")
00007 #define CR (0x0D)
00008 #define RESP_OK ("OK")
00009 
00010 #define XBEE_START_DEL (0x7E)
00011 
00012 #define XBEE_API_ID_TX_64    (0x00)
00013 #define XBEE_API_ID_AT_CMD   (0x08)
00014 #define XBEE_API_ID_AT_RESP  (0x88)
00015 #define XBEE_API_ID_TX_STAT  (0x89)
00016 #define XBEE_API_ID_RX_64    (0x80)
00017 #define XBEE_API_ID_RX_16    (0x81)
00018 #define XBEE_API_ID_MOD_STAT (0x8A)
00019 
00020 #define XBEE_MOD_STAT_HW_RESET    (0)
00021 #define XBEE_MOD_STAT_WD_RESET    (1)
00022 #define XBEE_MOD_STAT_ASSOC       (2)
00023 #define XBEE_MOD_STAT_DIASSOC     (3)
00024 #define XBEE_MOD_STAT_SYNC_LOST   (4)
00025 #define XBEE_MOD_STAT_COORD_REALG (5)
00026 #define XBEE_MOD_STAT_COORD_START (6)
00027 
00028 #define XBEE_TX_STAT_SUCCESS  (0)
00029 #define XBEE_TX_STAT_NOACK    (1)
00030 #define XBEE_TX_STAT_CCA_FAIL (2)
00031 #define XBEE_TX_STAT_PURGED   (3)
00032 
00033 #define XBEE_RECV_FRAME_TO (2000)
00034 
00035 XBee::XBee(PinName tx, PinName rx, PinName reset, PinName sleep) :
00036     _serial(tx, rx), _reset(reset), _sleep(sleep) {
00037     _serial.baud(9600);
00038     //_serial.format(8, Serial::None, 1);
00039 
00040     _serial.attach(this, &XBee::uartRxIrq, Serial::RxIrq);
00041 
00042     rxqIn = 0;
00043     rxqOut = 0;
00044     _rfFrameTimeout = 0;
00045     _rfFrameTimer.reset();
00046 
00047     _rfState = RfStateFrame;
00048     _rfPos = 0;
00049     _rfFrameLen = 0;
00050     _rfFrameId = 0;
00051 
00052     _initialized = false;
00053     _type = Coordinator;
00054 
00055     _addrHi = 0;
00056     _addrLo = 0;
00057     _rssi = 0;
00058     _frameId = 0;
00059     _txStatus = TxStatusOk;
00060     _recvData = NULL;
00061     _recvLen = 0;
00062 
00063     // we enter sleep when sleep pin is high
00064     _sleep = 0;
00065 }
00066 
00067 XBee::XBeeError XBee::init(XBeeType type, const char* panId) {
00068     XBeeError err = Ok;
00069     char idBuf[10];
00070 
00071     resetModule();
00072 
00073    _type = type;
00074 
00075     do {
00076 
00077         if (panId == NULL || strlen(panId) != 4) {
00078             err = ArgumentError;
00079             break;
00080         }
00081 
00082         err = commandMode();
00083         if (err != Ok) {
00084             debug("XBee: +++ failed\n");
00085             break;
00086         }
00087 
00088         // set the sleep mode to Hibernate
00089         err = atSet("ATSM1");
00090         if (err != Ok) {
00091             debug("Xbee: ATSM1 failed\r\n");
00092             break;
00093         }
00094 
00095         // change PAN ID to EAEA
00096         sprintf(idBuf, "ATID%s", panId);
00097         err = atSet(idBuf);
00098         if (err != Ok) {
00099             debug("Xbee: ATID failed\r\n");
00100             break;
00101         }
00102 
00103         // use 64-bit addressing
00104         err = atSet("ATMYFFFE");
00105         if (err != Ok) {
00106             debug("Xbee: ATMYFFFE failed\r\n");
00107             break;
00108         }
00109 
00110         // Coordinator
00111         if (type == Coordinator) {
00112 
00113             // - will not perform Active Scan to locate PAN ID
00114             // - will not perform Energy Scan to determine free channel
00115             // - allow End devices to associate with this coordinator
00116             err = atSet("ATA24");
00117             if (err != Ok) {
00118                 debug("Xbee: ATA24 failed\r\n");
00119                 break;
00120             }
00121 
00122             // set this node as coordinator
00123             err = atSet("ATCE1");
00124             if (err != Ok) {
00125                 debug("Xbee: ATCE1 failed\r\n");
00126                 break;
00127             }
00128         }
00129         else {
00130             // - only associates with Coordinator on matching PAN ID
00131             // - only associates with Coordinator on matching channel
00132             // - device attempts association until success
00133             err = atSet("ATA14");
00134             if (err != Ok) {
00135                 debug("Xbee: ATA14 failed\r\n");
00136                 break;
00137             }
00138         }
00139 
00140 
00141         // change to API mode
00142         err = atSet("ATAP1");
00143         if (err != Ok) {
00144             debug("Xbee: ATAP1 failed\r\n");
00145             break;
00146         }
00147 
00148     } while(0);
00149 
00150 
00151     return err;
00152 }
00153 
00154 void XBee::process() {
00155     uint32_t len = 0;
00156     char data = 0;
00157 
00158     if (_rfFrameTimeout > 0 && (int)_rfFrameTimeout < _rfFrameTimer.read_ms()) {
00159         _rfState = RfStateFrame;
00160         debug("Xbee: Frame timer expired\r\n");
00161         _rfFrameTimeout = 0;
00162         _rfFrameTimer.stop();
00163     }
00164 
00165     if (!uartRxQIsEmpty()) {
00166 
00167         len = uartReceive(&data, 1);
00168         if (len > 0) {
00169             processByte(data);
00170         }
00171 
00172     }
00173 }
00174 
00175 XBee::XBeeError XBee::getRemoteAddress(uint32_t* addrHi, uint32_t* addrLo) {
00176     if (!_initialized) {
00177         return NotInitializedError;
00178     }
00179 
00180     if (addrHi == NULL || addrLo == NULL) {
00181         return ArgumentError;
00182     }
00183 
00184     *addrHi = _addrHi;
00185     *addrLo = _addrLo;
00186 
00187     return Ok;
00188 }
00189 
00190 XBee::XBeeError XBee::getRssi(uint8_t* rssi ) {
00191     if (!_initialized) {
00192         return NotInitializedError;
00193     }
00194 
00195     if (rssi == NULL) {
00196         return ArgumentError;
00197     }
00198 
00199     *rssi = _rssi;
00200 
00201     return Ok;
00202 }
00203 
00204 XBee::XBeeError XBee::getTxStatus(uint8_t* frameId, XBeeTxStatus* status) {
00205     if (!_initialized) {
00206         return NotInitializedError;
00207     }
00208 
00209     if (frameId == NULL || status == NULL) {
00210         return ArgumentError;
00211     }
00212 
00213     *frameId = _frameId;
00214     *status = _txStatus;
00215 
00216     return Ok;
00217 }
00218 
00219 XBee::XBeeError XBee::getData(char** data, uint8_t* len) {
00220     if (!_initialized) {
00221         return NotInitializedError;
00222     }
00223 
00224     if (data == NULL || len == NULL) {
00225         return ArgumentError;
00226     }
00227 
00228     *data = _recvData;
00229     *len = _recvLen;
00230 
00231     return Ok;
00232 }
00233 
00234 XBee::XBeeError XBee::send(uint32_t addrHi, uint32_t addrLo, char* data,
00235     uint8_t len, uint8_t* frameId)
00236 {
00237   if (!_initialized) {
00238     return NotInitializedError;
00239   }
00240 
00241   return apiTx64(addrHi, addrLo, data, len, frameId);
00242 }
00243 
00244 XBee::XBeeError XBee::discoverNodes() {
00245     if (!_initialized) {
00246       return NotInitializedError;
00247     }
00248 
00249     return apiAtCmd("ND", 0, false);
00250 }
00251 
00252 
00253 XBee::XBeeError XBee::enterSleep() {
00254     _sleep = 1;
00255 
00256     return Ok;
00257 }
00258 
00259 XBee::XBeeError XBee::exitSleep() {
00260     _sleep = 0;
00261 
00262     return Ok;
00263 }
00264 
00265 
00266 void XBee::uartRxIrq() {
00267     while(_serial.readable()) {
00268         uartRxQPut(_serial.getc());
00269     }
00270 }
00271 
00272 void XBee::uartRxQPut(uint8_t data)
00273 {
00274   // full
00275   if (rxqOut == (rxqIn + 1) % RX_BUF_SIZE) {
00276     return;
00277   }
00278 
00279   rxq[rxqIn] = data;
00280   rxqIn = (rxqIn + 1) % RX_BUF_SIZE;
00281 }
00282 
00283 uint8_t XBee::uartRxQGet()
00284 {
00285   uint8_t d = 0;
00286   // empty
00287   if (rxqIn == rxqOut) {
00288     return 0;
00289   }
00290 
00291   d = rxq[rxqOut];
00292   rxqOut = (rxqOut + 1) % RX_BUF_SIZE;
00293 
00294   return d;
00295 }
00296 
00297 bool XBee::uartRxQIsEmpty()
00298 {
00299   return (rxqIn == rxqOut);
00300 }
00301 
00302 uint32_t XBee::uartReceive(char *buf, uint32_t buflen)
00303 {
00304 
00305   uint32_t pos = 0;
00306 
00307   while(buflen > 0 && !uartRxQIsEmpty()) {
00308     buf[pos] = uartRxQGet();
00309     pos++;
00310     buflen--;
00311   }
00312 
00313   return pos;
00314 }
00315 
00316 int32_t XBee::uartReadLine(char* buf, uint32_t bufLen, uint32_t timeout)
00317 {
00318   uint32_t pos = 0;
00319   uint32_t len = 0;
00320   Timer tim;
00321 
00322   tim.reset();
00323   tim.start();
00324 
00325   while(pos < bufLen && tim.read_ms() < (int)timeout) {
00326 
00327     len = uartReceive(&buf[pos], 1);
00328     if (len > 0 && buf[pos] == CR) {
00329       buf[pos] = '\0';
00330       break;
00331     }
00332 
00333     pos += len;
00334   }
00335 
00336   if (pos >= bufLen) {
00337     return BufTooSmallError;
00338   }
00339 
00340   if (tim.read_ms() > (int)timeout) {
00341     return TimeOutError;
00342   }
00343 
00344   return pos;
00345 }
00346 
00347 void XBee::resetModule() {
00348     // reset pulse must be at least 200 ns. Using 1 ms.
00349     _reset = 0;
00350     wait_ms(1);
00351     _reset = 1;
00352 
00353     // wait to make sure the module has started
00354     wait_ms(500);
00355     rxqIn = 0;
00356     rxqOut = 0;
00357 }
00358 
00359 XBee::XBeeError XBee::commandMode() {
00360     XBeeError err = Ok;
00361     int32_t lineLen = 0;
00362     char ebuf[10];
00363 
00364     _serial.printf("+++");
00365 
00366     lineLen = uartReadLine(ebuf, 10, 1200);
00367 
00368     do {
00369       if (lineLen < 0) {
00370         // error while reading
00371         err = ReadError;
00372         break;
00373       }
00374 
00375       if (strcmp(RESP_OK, (char*)ebuf) != 0) {
00376         // didn't receive OK
00377         err = CmdError;
00378         break;
00379       }
00380     } while(0);
00381 
00382     return err;
00383 }
00384 
00385 XBee::XBeeError XBee::atGet(const char* atCmd, char* resp, uint32_t respLen)
00386 {
00387 
00388     int32_t lineLen = 0;
00389     XBeeError err = Ok;
00390 
00391     _serial.printf("%s%s", atCmd, XBEE_END_CMD);
00392 
00393     do {
00394 
00395       // a response is expected
00396       if (resp != NULL && respLen > 0) {
00397         lineLen = uartReadLine(resp, respLen, 1000);
00398 
00399         if (lineLen < 0) {
00400           // error while reading
00401           err = ReadError;
00402           break;
00403         }
00404 
00405       }
00406 
00407     } while(0);
00408 
00409     return err;
00410 }
00411 
00412 XBee::XBeeError XBee::atSet(const char* atCmd)
00413 {
00414   char b[10];
00415   XBeeError err = Ok;
00416 
00417   err = atGet(atCmd, b, 10);
00418   if (err == Ok) {
00419 
00420     if (strcmp(RESP_OK, (char*)b) != 0) {
00421       // didn't receive OK
00422       err = CmdError;
00423     }
00424   }
00425 
00426   return err;
00427 }
00428 
00429 void XBee::processByte(char data)
00430 {
00431     switch(_rfState) {
00432     case RfStateFrame:
00433         if (data == XBEE_START_DEL) {
00434             _rfPos = 0;
00435             _rfFrameLen = 0;
00436             _rfState = RfStateLength;
00437 
00438             // start timer to make sure an entire frame is received
00439             // within a specific time
00440             _rfFrameTimeout = XBEE_RECV_FRAME_TO;
00441             _rfFrameTimer.reset();
00442             _rfFrameTimer.start();
00443         }
00444 
00445         break;
00446     case RfStateLength:
00447         _rfFrameLen |= (data << (8*(1-_rfPos)));
00448         _rfPos++;
00449         if (_rfPos == 2) {
00450             _rfPos = 0;
00451             _rfState = RfStateData;
00452 
00453             if (_rfFrameLen > XBEE_BUF_SZ) {
00454                 debug("Xbee: Frame len %d > max buffer len %d\r\n",
00455                         (int)_rfFrameLen, (int)XBEE_BUF_SZ);
00456                 _rfFrameLen = XBEE_BUF_SZ;
00457             }
00458 
00459         }
00460         break;
00461     case RfStateData:
00462         _rfBuf[_rfPos++] = data;
00463         // read up until checksum (1 byte)
00464         if (_rfPos == _rfFrameLen+1) {
00465             _rfState = RfStateFrame;
00466 
00467             // cancel timer
00468             _rfFrameTimeout = 0;
00469             _rfFrameTimer.stop();
00470 
00471             processFrame(_rfBuf, _rfPos);
00472         }
00473         break;
00474 
00475     }
00476 }
00477 
00478 void XBee::processFrame(char* buf, uint32_t len)
00479 {
00480 
00481     uint32_t addrLo = 0;
00482     uint32_t addrHi = 0;
00483     char* b = NULL;
00484     uint32_t bLen = 0;
00485 
00486     if (len < 2) {
00487         debug("Xbee: Invalid frame length (%d)\r\n", (int)len);
00488         return;
00489     }
00490 
00491     // verify checksum
00492     if (checksum(buf, len) != 0) {
00493         debug("Xbee: Invalid checksum\r\n");
00494         return;
00495     }
00496 
00497     switch(buf[0]) {
00498     case XBEE_API_ID_AT_RESP:
00499         if (len < 5) {
00500             debug("Xbee: AT resp data too small: %d\r\n ", (int)len);
00501             return;
00502         }
00503 
00504         // there is a value
00505         if (len > 6) {
00506             b = &buf[5];
00507             bLen = len-5-1;
00508         }
00509 
00510         handleAtResponse(buf[1], &buf[2], buf[4], b, bLen);
00511         break;
00512     case XBEE_API_ID_TX_STAT:
00513         handleTxStatus(buf[1], buf[2]);
00514         break;
00515     case XBEE_API_ID_RX_64:
00516         if (len < 12) {
00517             debug("Xbee: RX data too small: %d\r\n ", (int)len);
00518             return;
00519         }
00520         addrHi = bufTo32bitInt(&buf[1]);
00521         addrLo = bufTo32bitInt(&buf[5]);
00522 
00523         processData(addrHi, addrLo, buf[9], buf[10], &buf[11], len-11-1);
00524         break;
00525     case XBEE_API_ID_RX_16:
00526         debug("Xbee: RX 16 bit (unhandled)\r\n");
00527         break;
00528     case XBEE_API_ID_MOD_STAT:
00529         handleModemStatus(buf[1]);
00530         break;
00531     default:
00532         debug("Xbee: Unhandled API ID: %x\r\n ", buf[0]);
00533 
00534         break;
00535     }
00536 
00537 }
00538 
00539 void XBee::handleAtResponse(uint8_t frameId, char* atBuf, uint8_t status,
00540     char* valueBuf, uint32_t valueLen)
00541 {
00542 
00543   if (strncmp("ND", (char*)atBuf, 2) == 0) {
00544     handleDiscovery(status, valueBuf, valueLen);
00545   }
00546 
00547 }
00548 
00549 void XBee::handleDiscovery(uint8_t status, char* buf, uint32_t len)
00550 {
00551 
00552   if (status == 0 && len >= 11) {
00553     _addrHi = bufTo32bitInt(&buf[2]);
00554     _addrLo = bufTo32bitInt(&buf[6]);
00555     _rssi = buf[10];
00556 
00557     _callbacks[CbNodeFound].call();
00558 
00559   }
00560 }
00561 
00562 void XBee::handleTxStatus(uint8_t frameId, uint8_t status)
00563 {
00564     _frameId = frameId;
00565     switch(status)  {
00566     case XBEE_TX_STAT_SUCCESS:
00567       _txStatus = TxStatusOk;
00568       break;
00569     case XBEE_TX_STAT_NOACK:
00570       _txStatus = TxStatusNoAck;
00571       break;
00572     case XBEE_TX_STAT_CCA_FAIL:
00573       _txStatus = TxStatusCCA;
00574       break;
00575     case XBEE_TX_STAT_PURGED:
00576       _txStatus = TxStatusPurged;
00577       break;
00578     }
00579 
00580     _callbacks[CbTxStat].call();
00581 
00582 }
00583 
00584 void XBee::processData(uint32_t addrHi, uint32_t addrLo, uint8_t rssi,
00585     uint8_t opt, char* buf, uint32_t len)
00586 {
00587     _addrHi = addrHi;
00588     _addrLo = addrLo;
00589     _rssi = rssi;
00590     _recvData = buf;
00591     _recvLen = len;
00592 
00593     _callbacks[CbDataAvailable].call();
00594 }
00595 
00596 void XBee::handleModemStatus(uint8_t status) {
00597 
00598     if (_type == Coordinator && status == XBEE_MOD_STAT_COORD_START) {
00599         _initialized = true;
00600         _callbacks[CbDeviceUp].call();
00601     }
00602     else if (_type == EndDevice && status == XBEE_MOD_STAT_ASSOC) {
00603         _initialized = 1;
00604         _callbacks[CbDeviceUp].call();
00605     }
00606     else if (_type == EndDevice && status == XBEE_MOD_STAT_DIASSOC) {
00607         _initialized = false;
00608         _callbacks[CbDeviceDown].call();
00609     }
00610 
00611 }
00612 
00613 char XBee::checksum(char* buf, uint32_t len)
00614 {
00615     int i = 0;
00616     char cs = 0;
00617 
00618     for (i = 0; i < (int)len; i++) {
00619         cs += buf[i];
00620     }
00621 
00622     return (0xFF - cs);
00623 }
00624 
00625 uint32_t XBee::bufTo32bitInt(const char* buf)
00626 {
00627     uint32_t v = 0;
00628 
00629     v |= (buf[0] << 24);
00630     v |= (buf[1] << 16);
00631     v |= (buf[2] << 8);
00632     v |= (buf[3]);
00633 
00634     return v;
00635 }
00636 
00637 void XBee::int32bitToBuf(uint32_t v, char* buf)
00638 {
00639 
00640   buf[0] = ((v >> 24) & 0xff);
00641   buf[1] = ((v >> 16) & 0xff);
00642   buf[2] = ((v >> 8) & 0xff);
00643   buf[3] = ((v >> 0) & 0xff);
00644 
00645 }
00646 
00647 
00648 XBee::XBeeError XBee::apiTx64(uint32_t addrHi, uint32_t addrLo, char* data,
00649     uint32_t len, uint8_t* frameId)
00650 {
00651     char buf[100];
00652 
00653 
00654     // limiting to 85 bytes data. Remaining 15 bytes belong to the
00655     // frame
00656     if (len > 85) {
00657         return ArgumentError;
00658     }
00659 
00660     buf[0] = XBEE_START_DEL;
00661 
00662     // length
00663     buf[1] = 0;
00664     buf[2] = 11+len;
00665 
00666     // AP ID
00667     buf[3] = XBEE_API_ID_TX_64;
00668 
00669     // frame ID
00670     buf[4] = getFrameId();
00671 
00672 
00673     // address
00674     int32bitToBuf(addrHi, &buf[5]);
00675     int32bitToBuf(addrLo, &buf[9]);
00676 
00677     // options
00678     buf[13] = 0;
00679 
00680     // data
00681     memcpy(&buf[14], data, len);
00682 
00683     // checksum
00684     buf[14+len] = checksum(&buf[3], buf[2]);
00685 
00686     if (frameId != NULL) {
00687         *frameId = buf[4];
00688     }
00689 
00690 
00691     for (int i = 0; i < (15+len); i++) {
00692         _serial.putc(buf[i]);
00693     }
00694 
00695 
00696     return Ok;
00697 }
00698 
00699 XBee::XBeeError XBee::apiAtCmd(const char* atCmd, uint32_t param, bool useParameter)
00700 {
00701     char buf[12];
00702     int pos = 0;
00703 
00704     buf[0] = XBEE_START_DEL;
00705 
00706     // length
00707     buf[1] = 0;
00708     buf[2] = 4;
00709     if (useParameter) {
00710       buf[2] += 4;
00711     }
00712 
00713     // AP ID
00714     buf[3] = XBEE_API_ID_AT_CMD;
00715 
00716     // frame ID
00717     buf[4] = getFrameId();
00718 
00719     // AT cmd
00720     buf[5] = atCmd[0];
00721     buf[6] = atCmd[1];
00722     pos = 7;
00723 
00724     // AT parameter
00725     if (useParameter) {
00726       buf[pos++] = ((param >> 24) & 0xff);
00727       buf[pos++] = ((param >> 16) & 0xff);
00728       buf[pos++] = ((param >> 8) & 0xff);
00729       buf[pos++] = ((param >> 0) & 0xff);
00730     }
00731 
00732     // checksum
00733     buf[pos] = checksum(&buf[3], pos-3);
00734     pos++;
00735 
00736     for (int i = 0; i < pos; i++) {
00737         _serial.putc(buf[i]);
00738     }
00739 
00740     return Ok;
00741 }
00742 
00743 uint8_t XBee::getFrameId(void)
00744 {
00745     _rfFrameId++;
00746     if (_rfFrameId == 0) {
00747         _rfFrameId = 1;
00748     }
00749 
00750     return _rfFrameId;
00751 }