A library with drivers for different peripherals on the LPC4088 QuickStart Board or related add-on boards.
Dependents: LPC4088test LPC4088test_ledonly LPC4088test_deleteall LPC4088_RAMtest ... more
XBee.cpp
- Committer:
- embeddedartists
- Date:
- 2013-11-11
- Revision:
- 7:e431d9d47db6
- Child:
- 12:15597e45eea0
File content as of revision 7:e431d9d47db6:
#include "mbed.h" #include "mbed_debug.h" #include "XBee.h" #define XBEE_END_CMD ("\r") #define CR (0x0D) #define RESP_OK ("OK") #define XBEE_START_DEL (0x7E) #define XBEE_API_ID_TX_64 (0x00) #define XBEE_API_ID_AT_CMD (0x08) #define XBEE_API_ID_AT_RESP (0x88) #define XBEE_API_ID_TX_STAT (0x89) #define XBEE_API_ID_RX_64 (0x80) #define XBEE_API_ID_RX_16 (0x81) #define XBEE_API_ID_MOD_STAT (0x8A) #define XBEE_MOD_STAT_HW_RESET (0) #define XBEE_MOD_STAT_WD_RESET (1) #define XBEE_MOD_STAT_ASSOC (2) #define XBEE_MOD_STAT_DIASSOC (3) #define XBEE_MOD_STAT_SYNC_LOST (4) #define XBEE_MOD_STAT_COORD_REALG (5) #define XBEE_MOD_STAT_COORD_START (6) #define XBEE_TX_STAT_SUCCESS (0) #define XBEE_TX_STAT_NOACK (1) #define XBEE_TX_STAT_CCA_FAIL (2) #define XBEE_TX_STAT_PURGED (3) #define XBEE_RECV_FRAME_TO (2000) XBee::XBee(PinName tx, PinName rx, PinName reset, PinName sleep) : _serial(tx, rx), _reset(reset), _sleep(sleep) { _serial.baud(9600); _serial.format(8, Serial::None, 1); _serial.attach(this, &XBee::uartRxIrq, Serial::RxIrq); rxqIn = 0; rxqOut = 0; _rfFrameTimeout = 0; _rfFrameTimer.reset(); _rfState = RfStateFrame; _rfPos = 0; _rfFrameLen = 0; _rfFrameId = 0; _initialized = false; _type = Coordinator; _addrHi = 0; _addrLo = 0; _rssi = 0; _frameId = 0; _txStatus = TxStatusOk; _recvData = NULL; _recvLen = 0; // we enter sleep when sleep pin is high _sleep = 0; } XBee::XBeeError XBee::init(XBeeType type, const char* panId) { XBeeError err = Ok; char idBuf[10]; resetModule(); _type = type; do { if (panId == NULL || strlen(panId) != 4) { err = ArgumentError; break; } err = commandMode(); if (err != Ok) { debug("XBee: +++ failed\n"); break; } // set the sleep mode to Hibernate err = atSet("ATSM1"); if (err != Ok) { debug("Xbee: ATSM1 failed\r\n"); break; } // change PAN ID to EAEA sprintf(idBuf, "ATID%s", panId); err = atSet(idBuf); if (err != Ok) { debug("Xbee: ATID failed\r\n"); break; } // use 64-bit addressing err = atSet("ATMYFFFE"); if (err != Ok) { debug("Xbee: ATMYFFFE failed\r\n"); break; } // Coordinator if (type == Coordinator) { // - will not perform Active Scan to locate PAN ID // - will not perform Energy Scan to determine free channel // - allow End devices to associate with this coordinator err = atSet("ATA24"); if (err != Ok) { debug("Xbee: ATA24 failed\r\n"); break; } // set this node as coordinator err = atSet("ATCE1"); if (err != Ok) { debug("Xbee: ATCE1 failed\r\n"); break; } } else { // - only associates with Coordinator on matching PAN ID // - only associates with Coordinator on matching channel // - device attempts association until success err = atSet("ATA14"); if (err != Ok) { debug("Xbee: ATA14 failed\r\n"); break; } } // change to API mode err = atSet("ATAP1"); if (err != Ok) { debug("Xbee: ATAP1 failed\r\n"); break; } } while(0); return err; } void XBee::process() { uint32_t len = 0; char data = 0; if (_rfFrameTimeout > 0 && (int)_rfFrameTimeout < _rfFrameTimer.read_ms()) { _rfState = RfStateFrame; debug("Xbee: Frame timer expired\r\n"); _rfFrameTimeout = 0; _rfFrameTimer.stop(); } if (!uartRxQIsEmpty()) { len = uartReceive(&data, 1); if (len > 0) { processByte(data); } } } XBee::XBeeError XBee::getRemoteAddress(uint32_t* addrHi, uint32_t* addrLo) { if (!_initialized) { return NotInitializedError; } if (addrHi == NULL || addrLo == NULL) { return ArgumentError; } *addrHi = _addrHi; *addrLo = _addrLo; return Ok; } XBee::XBeeError XBee::getRssi(uint8_t* rssi ) { if (!_initialized) { return NotInitializedError; } if (rssi == NULL) { return ArgumentError; } *rssi = _rssi; return Ok; } XBee::XBeeError XBee::getTxStatus(uint8_t* frameId, XBeeTxStatus* status) { if (!_initialized) { return NotInitializedError; } if (frameId == NULL || status == NULL) { return ArgumentError; } *frameId = _frameId; *status = _txStatus; return Ok; } XBee::XBeeError XBee::getData(char** data, uint8_t* len) { if (!_initialized) { return NotInitializedError; } if (data == NULL || len == NULL) { return ArgumentError; } *data = _recvData; *len = _recvLen; return Ok; } XBee::XBeeError XBee::send(uint32_t addrHi, uint32_t addrLo, char* data, uint8_t len, uint8_t* frameId) { if (!_initialized) { return NotInitializedError; } return apiTx64(addrHi, addrLo, data, len, frameId); } XBee::XBeeError XBee::discoverNodes() { if (!_initialized) { return NotInitializedError; } return apiAtCmd("ND", 0, false); } XBee::XBeeError XBee::enterSleep() { _sleep = 1; return Ok; } XBee::XBeeError XBee::exitSleep() { _sleep = 0; return Ok; } void XBee::uartRxIrq() { while(_serial.readable()) { uartRxQPut(_serial.getc()); } } void XBee::uartRxQPut(uint8_t data) { // full if (rxqOut == (rxqIn + 1) % RX_BUF_SIZE) { return; } rxq[rxqIn] = data; rxqIn = (rxqIn + 1) % RX_BUF_SIZE; } uint8_t XBee::uartRxQGet() { uint8_t d = 0; // empty if (rxqIn == rxqOut) { return 0; } d = rxq[rxqOut]; rxqOut = (rxqOut + 1) % RX_BUF_SIZE; return d; } bool XBee::uartRxQIsEmpty() { return (rxqIn == rxqOut); } uint32_t XBee::uartReceive(char *buf, uint32_t buflen) { uint32_t pos = 0; while(buflen > 0 && !uartRxQIsEmpty()) { buf[pos] = uartRxQGet(); pos++; buflen--; } return pos; } int32_t XBee::uartReadLine(char* buf, uint32_t bufLen, uint32_t timeout) { uint32_t pos = 0; uint32_t len = 0; Timer tim; tim.reset(); tim.start(); while(pos < bufLen && tim.read_ms() < (int)timeout) { len = uartReceive(&buf[pos], 1); if (len > 0 && buf[pos] == CR) { buf[pos] = '\0'; break; } pos += len; } if (pos >= bufLen) { return BufTooSmallError; } if (tim.read_ms() > (int)timeout) { return TimeOutError; } return pos; } void XBee::resetModule() { // reset pulse must be at least 200 ns. Using 1 ms. _reset = 0; wait_ms(1); _reset = 1; // wait to make sure the module has started wait_ms(500); rxqIn = 0; rxqOut = 0; } XBee::XBeeError XBee::commandMode() { XBeeError err = Ok; int32_t lineLen = 0; char ebuf[10]; _serial.printf("+++"); lineLen = uartReadLine(ebuf, 10, 1200); do { if (lineLen < 0) { // error while reading err = ReadError; break; } if (strcmp(RESP_OK, (char*)ebuf) != 0) { // didn't receive OK err = CmdError; break; } } while(0); return err; } XBee::XBeeError XBee::atGet(const char* atCmd, char* resp, uint32_t respLen) { int32_t lineLen = 0; XBeeError err = Ok; _serial.printf("%s%s", atCmd, XBEE_END_CMD); do { // a response is expected if (resp != NULL && respLen > 0) { lineLen = uartReadLine(resp, respLen, 1000); if (lineLen < 0) { // error while reading err = ReadError; break; } } } while(0); return err; } XBee::XBeeError XBee::atSet(const char* atCmd) { char b[10]; XBeeError err = Ok; err = atGet(atCmd, b, 10); if (err == Ok) { if (strcmp(RESP_OK, (char*)b) != 0) { // didn't receive OK err = CmdError; } } return err; } void XBee::processByte(char data) { switch(_rfState) { case RfStateFrame: if (data == XBEE_START_DEL) { _rfPos = 0; _rfFrameLen = 0; _rfState = RfStateLength; // start timer to make sure an entire frame is received // within a specific time _rfFrameTimeout = XBEE_RECV_FRAME_TO; _rfFrameTimer.reset(); _rfFrameTimer.start(); } break; case RfStateLength: _rfFrameLen |= (data << (8*(1-_rfPos))); _rfPos++; if (_rfPos == 2) { _rfPos = 0; _rfState = RfStateData; if (_rfFrameLen > XBEE_BUF_SZ) { debug("Xbee: Frame len %d > max buffer len %d\r\n", (int)_rfFrameLen, (int)XBEE_BUF_SZ); _rfFrameLen = XBEE_BUF_SZ; } } break; case RfStateData: _rfBuf[_rfPos++] = data; // read up until checksum (1 byte) if (_rfPos == _rfFrameLen+1) { _rfState = RfStateFrame; // cancel timer _rfFrameTimeout = 0; _rfFrameTimer.stop(); processFrame(_rfBuf, _rfPos); } break; } } void XBee::processFrame(char* buf, uint32_t len) { uint32_t addrLo = 0; uint32_t addrHi = 0; char* b = NULL; uint32_t bLen = 0; if (len < 2) { debug("Xbee: Invalid frame length (%d)\r\n", (int)len); return; } // verify checksum if (checksum(buf, len) != 0) { debug("Xbee: Invalid checksum\r\n"); return; } switch(buf[0]) { case XBEE_API_ID_AT_RESP: if (len < 5) { debug("Xbee: AT resp data too small: %d\r\n ", (int)len); return; } // there is a value if (len > 6) { b = &buf[5]; bLen = len-5-1; } handleAtResponse(buf[1], &buf[2], buf[4], b, bLen); break; case XBEE_API_ID_TX_STAT: handleTxStatus(buf[1], buf[2]); break; case XBEE_API_ID_RX_64: if (len < 12) { debug("Xbee: RX data too small: %d\r\n ", (int)len); return; } addrHi = bufTo32bitInt(&buf[1]); addrLo = bufTo32bitInt(&buf[5]); processData(addrHi, addrLo, buf[9], buf[10], &buf[11], len-11-1); break; case XBEE_API_ID_RX_16: debug("Xbee: RX 16 bit (unhandled)\r\n"); break; case XBEE_API_ID_MOD_STAT: handleModemStatus(buf[1]); break; default: debug("Xbee: Unhandled API ID: %x\r\n ", buf[0]); break; } } void XBee::handleAtResponse(uint8_t frameId, char* atBuf, uint8_t status, char* valueBuf, uint32_t valueLen) { if (strncmp("ND", (char*)atBuf, 2) == 0) { handleDiscovery(status, valueBuf, valueLen); } } void XBee::handleDiscovery(uint8_t status, char* buf, uint32_t len) { if (status == 0 && len >= 11) { _addrHi = bufTo32bitInt(&buf[2]); _addrLo = bufTo32bitInt(&buf[6]); _rssi = buf[10]; _callbacks[CbNodeFound].call(); } } void XBee::handleTxStatus(uint8_t frameId, uint8_t status) { _frameId = frameId; switch(status) { case XBEE_TX_STAT_SUCCESS: _txStatus = TxStatusOk; break; case XBEE_TX_STAT_NOACK: _txStatus = TxStatusNoAck; break; case XBEE_TX_STAT_CCA_FAIL: _txStatus = TxStatusCCA; break; case XBEE_TX_STAT_PURGED: _txStatus = TxStatusPurged; break; } _callbacks[CbTxStat].call(); } void XBee::processData(uint32_t addrHi, uint32_t addrLo, uint8_t rssi, uint8_t opt, char* buf, uint32_t len) { _addrHi = addrHi; _addrLo = addrLo; _rssi = rssi; _recvData = buf; _recvLen = len; _callbacks[CbDataAvailable].call(); } void XBee::handleModemStatus(uint8_t status) { if (_type == Coordinator && status == XBEE_MOD_STAT_COORD_START) { _initialized = true; _callbacks[CbDeviceUp].call(); } else if (_type == EndDevice && status == XBEE_MOD_STAT_ASSOC) { _initialized = 1; _callbacks[CbDeviceUp].call(); } else if (_type == EndDevice && status == XBEE_MOD_STAT_DIASSOC) { _initialized = false; _callbacks[CbDeviceDown].call(); } } char XBee::checksum(char* buf, uint32_t len) { int i = 0; char cs = 0; for (i = 0; i < (int)len; i++) { cs += buf[i]; } return (0xFF - cs); } uint32_t XBee::bufTo32bitInt(const char* buf) { uint32_t v = 0; v |= (buf[0] << 24); v |= (buf[1] << 16); v |= (buf[2] << 8); v |= (buf[3]); return v; } void XBee::int32bitToBuf(uint32_t v, char* buf) { buf[0] = ((v >> 24) & 0xff); buf[1] = ((v >> 16) & 0xff); buf[2] = ((v >> 8) & 0xff); buf[3] = ((v >> 0) & 0xff); } XBee::XBeeError XBee::apiTx64(uint32_t addrHi, uint32_t addrLo, char* data, uint32_t len, uint8_t* frameId) { char buf[100]; // limiting to 85 bytes data. Remaining 15 bytes belong to the // frame if (len > 85) { return ArgumentError; } buf[0] = XBEE_START_DEL; // length buf[1] = 0; buf[2] = 11+len; // AP ID buf[3] = XBEE_API_ID_TX_64; // frame ID buf[4] = getFrameId(); // address int32bitToBuf(addrHi, &buf[5]); int32bitToBuf(addrLo, &buf[9]); // options buf[13] = 0; // data memcpy(&buf[14], data, len); // checksum buf[14+len] = checksum(&buf[3], buf[2]); if (frameId != NULL) { *frameId = buf[4]; } for (int i = 0; i < (15+len); i++) { _serial.putc(buf[i]); } return Ok; } XBee::XBeeError XBee::apiAtCmd(const char* atCmd, uint32_t param, bool useParameter) { char buf[12]; int pos = 0; buf[0] = XBEE_START_DEL; // length buf[1] = 0; buf[2] = 4; if (useParameter) { buf[2] += 4; } // AP ID buf[3] = XBEE_API_ID_AT_CMD; // frame ID buf[4] = getFrameId(); // AT cmd buf[5] = atCmd[0]; buf[6] = atCmd[1]; pos = 7; // AT parameter if (useParameter) { buf[pos++] = ((param >> 24) & 0xff); buf[pos++] = ((param >> 16) & 0xff); buf[pos++] = ((param >> 8) & 0xff); buf[pos++] = ((param >> 0) & 0xff); } // checksum buf[pos] = checksum(&buf[3], pos-3); pos++; for (int i = 0; i < pos; i++) { _serial.putc(buf[i]); } return Ok; } uint8_t XBee::getFrameId(void) { _rfFrameId++; if (_rfFrameId == 0) { _rfFrameId = 1; } return _rfFrameId; }