Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Dependencies: CircularBuffer FixedLengthList
Dependents: XBeeApiTest XBeeApiSimpleATCmdsExample XBeeApiBroadcastExample XBeeApiBroadcastExampleRTOS ... more
Diff: Base/XBeeDevice.cpp
- Revision:
- 5:b40a6fd3a334
- Child:
- 6:3cb62daace78
diff -r e5e152b3930f -r b40a6fd3a334 Base/XBeeDevice.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Base/XBeeDevice.cpp Wed Jan 29 20:19:59 2014 +0000 @@ -0,0 +1,359 @@ +/** + +Copyright 2014 John Bailey + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +*/ + +#include "XBeeDevice.hpp" + +/** Number of bytes we need to 'peek' into the receive buffer in order to retrieve the + payload length */ +#define INITIAL_PEEK_LEN (3U) + +/** Enum of bytes with a special meaning when communicating with the XBee in API + mode. In escaped mode, these are the bytes that need to be escaped */ +typedef enum +{ + XBEE_SB_XON = 0x11, + XBEE_SB_XOFF = 0x13, + XBEE_SB_FRAME_DELIMITER = 0x7E, + XBEE_SB_ESCAPE = 0x7D +} XBeeSerialSpecialBytes_e; + +/** ASCII command to the XBee to request API mode 2 */ +const char api_mode2_cmd[] = { 'A', 'T', 'A', 'P', ' ', '2', '\r' }; + +/** ASCII command to the XBee to request that it exit command mode */ +const char exit_cmd_mode_cmd[] = { 'A', 'T', 'C', 'N', '\r' }; + +XBeeDevice::XBeeDevice( PinName p_tx, PinName p_rx, PinName p_rts, PinName p_cts ): m_if( p_tx, p_rx ) +{ + m_escape = true; + m_inAtCmdMode = false; + m_rxMsgLastWasEsc = false; + + /* Can only do flow control on devices which support it */ +#if defined ( DEVICE_SERIAL_FC ) + /* TODO: need rts and cts both set? */ + m_if.set_flow_control( mbed::SerialBase::Flow.RTSCTS, p_rts, p_cts ); +#endif + + /* Attach RX call-back to the serial interface */ + m_if.attach( this, &XBeeDevice::if_rx, Serial::RxIrq); +} + +XBeeDevice::~XBeeDevice( void ) +{ + /* Iterate all of the decoders and un-register them */ + for( FixedLengthList<XBeeApiFrameDecoder*, XBEEAPI_CONFIG_DECODER_LIST_SIZE>::iterator it = m_decoders.begin() ; + it != m_decoders.end(); + ++it ) { + (*it)->unregisterCallback(); + } +} + +void XBeeDevice::if_rx( void ) +{ + /* Keep going while there are bytes to be read */ + while(m_if.readable()) { + + uint8_t c = m_if.getc(); + + /* Sanity check that if we're starting from an empty buffer the byte that we're + receiving is a frame delimiter */ + if(( m_inAtCmdMode ) || + (( c == XBEE_SB_FRAME_DELIMITER ) || + ( m_rxBuff.getSize() ))) + { + /* If it's an escape character we want to de-code the escape, so flag + that we have a pending escape but don't modify the rx buffer */ + if( m_escape && + ( c == XBEE_SB_ESCAPE )) + { + m_rxMsgLastWasEsc = true; + } + else + { + if( m_rxMsgLastWasEsc ) { + c = c ^ 0x20; + m_rxMsgLastWasEsc = false; + } + m_rxBuff.write( &c, 1 ); + } + } else { + /* TODO */ + } + } + + if( m_inAtCmdMode ) + { + m_rxMsgLastWasEsc = false; + } + + checkRxDecode(); +} + +void XBeeDevice::checkRxDecode( void ) +{ + /* Get an initial portion of data from the read buffer so that the message length can be determined */ + uint8_t buff[INITIAL_PEEK_LEN]; + uint16_t len = m_rxBuff.peek( buff, INITIAL_PEEK_LEN ); + + if((len >= INITIAL_PEEK_LEN ) && + (buff[ XBEE_CMD_POSN_SDELIM ] == XBEE_SB_FRAME_DELIMITER )) + { + /* Try and get enough data to cover the whole message */ + /* TODO: Magic number */ + const uint16_t cmdLen = MSG_LEN_IN_BUFFER( buff ) + XBEE_API_FRAME_OVERHEAD; + uint8_t cmdBuff[cmdLen]; + uint16_t len = m_rxBuff.peek( cmdBuff, cmdLen ); + + if( len >= cmdLen ) + { + /* TODO: Verify checksum */ + + /* Iterate all of the decoders */ + for( FixedLengthList<XBeeApiFrameDecoder*, XBEEAPI_CONFIG_DECODER_LIST_SIZE>::iterator it = m_decoders.begin() ; + it != m_decoders.end(); + ++it ) { + + bool processed = (*it)->decodeCallback( cmdBuff, cmdLen ); + if( processed ) + { + break; + } + } + /* Remove the data from the receive buffer - either it was decoded (all well and good) + or it wasn't, in which case we need to get rid of it to prevent it from jamming + up the message queue */ + m_rxBuff.chomp( cmdLen ); + } + } +} + +bool XBeeDevice::registerDecoder( XBeeApiFrameDecoder* const p_decoder ) +{ + bool ret_val = false; + if( p_decoder != NULL ) + { + if( !m_decoders.inList( p_decoder ) ) { + m_decoders.push( p_decoder ); + p_decoder->registerCallback( this ); + ret_val = true; + } + } + return ret_val; +} + +bool XBeeDevice::unregisterDecoder( XBeeApiFrameDecoder* const p_decoder ) +{ + bool ret_val = false; + if( p_decoder != NULL ) + { + /* Iterate all of the decoders */ + for( FixedLengthList<XBeeApiFrameDecoder*, XBEEAPI_CONFIG_DECODER_LIST_SIZE>::iterator it = m_decoders.begin() ; + it != m_decoders.end(); + ++it ) { + if( *it == p_decoder ) { + p_decoder->unregisterCallback(); + /* TODO: Actually remove the item from m_decoders! */ + ret_val = true; + } + } + } + return ret_val; +} + +void XBeeDevice::SendFrame( const XBeeApiFrame* const p_cmd ) +{ + uint8_t sum = 0U; + uint16_t len; + uint16_t i; + const uint8_t* cmdData; + +#if defined XBEEAPI_CONFIG_USING_RTOS + m_ifMutex.lock(); +#endif + + xbeeWrite( XBEE_SB_FRAME_DELIMITER, false ); + + len = p_cmd->getCmdLen(); + xbeeWrite((uint8_t)(len >> 8U)); + xbeeWrite((uint8_t)(len & 0xFF)); + + sum += xbeeWrite((uint8_t)p_cmd->getApiId()); + + cmdData = p_cmd->getDataPtr(); + + for( i = 0; + i < (len-1); + i++ ) + { + sum += xbeeWrite(cmdData[i]); + } + + /* Checksum is 0xFF - summation of bytes (excluding delimiter and length) */ + xbeeWrite( (uint8_t)0xFFU - sum ); + + fflush( m_if ); + +#if defined XBEEAPI_CONFIG_USING_RTOS + m_ifMutex.unlock(); +#endif +} + +uint8_t XBeeDevice::xbeeWrite( uint8_t p_byte, bool p_doEscape ) +{ + uint8_t c_sum = 0; + + if (p_doEscape && m_escape && + ((p_byte == XBEE_SB_FRAME_DELIMITER ) || + (p_byte == XBEE_SB_ESCAPE ) || + (p_byte == XBEE_SB_XON ) || + (p_byte == XBEE_SB_XOFF))) + { + #if 0 + m_if.printf("%02x ",XBEE_SB_ESCAPE); + m_if.printf("%02x ",p_byte ^ 0x20); + #else + m_if.putc(XBEE_SB_ESCAPE); + m_if.putc(p_byte ^ 0x20); + #endif + c_sum += XBEE_SB_ESCAPE; + c_sum += p_byte ^ 0x20; + } else { + #if 0 + m_if.printf("%02x ",p_byte); + #else + m_if.putc(p_byte); + #endif + c_sum += p_byte; + } + return c_sum; +} + +#define IS_OK( _b ) (( _b[ 0 ] == 'O' ) && ( _b[ 1 ] == 'K' ) && ( _b[ 2 ] == '\r' )) +#define OK_LEN (3U) + +XBeeDevice::XBeeDeviceReturn_t XBeeDevice::SendFrame( const char* const p_dat, size_t p_len, int p_wait_ms ) +{ + XBeeDeviceReturn_t ret_val; + + if( m_inAtCmdMode ) + { +#if defined XBEEAPI_CONFIG_USING_RTOS + m_ifMutex.lock(); +#endif + for( size_t i = 0; + i < p_len; + i++ ) { + m_if.putc(p_dat[i]); + } + + fflush( m_if ); + + wait_ms( p_wait_ms ); + + /* Check the response for the OK indicator */ + if( m_rxBuff.getSize() == OK_LEN ) + { + uint8_t ok_buff[OK_LEN]; + m_rxBuff.read( ok_buff, OK_LEN ); + + if( IS_OK( ok_buff )) + { + ret_val = XBEEDEVICE_OK; + } + else + { + ret_val = XBEEDEVICE_UNEXPECTED_DATA; + } + } + else + { + ret_val = XBEEDEVICE_UNEXPECTED_LENGTH; + } +#if defined XBEEAPI_CONFIG_USING_RTOS + m_ifMutex.unlock(); +#endif + } + else + { + ret_val = XBEEDEVICE_WRONG_MODE; + } + return ret_val; +} + +XBeeDevice::XBeeDeviceReturn_t XBeeDevice::setUpApi( void ) +{ + XBeeDeviceReturn_t ret_val; + + /* Wait for the guard period before transmitting command sequence */ + wait_ms( XBEEAPI_CONFIG_GUARDPERIOD_MS ); + + m_inAtCmdMode = true; + + /* Request to enter command mode */ + /* TODO: Magic number */ + ret_val = SendFrame("+++", 3, 3000); + + /* Everything OK with last request? */ + if( ret_val == XBEEDEVICE_OK ) + { + wait_ms( XBEEAPI_CONFIG_GUARDPERIOD_MS ); + + /* API mode 2 please! */ + ret_val = SendFrame(api_mode2_cmd,sizeof(api_mode2_cmd)); + } + + /* Everything OK with last request? */ + if( ret_val == XBEEDEVICE_OK ) + { + /* Exit command mode, back to API mode */ + ret_val = SendFrame(exit_cmd_mode_cmd,sizeof(exit_cmd_mode_cmd)); + } + + m_inAtCmdMode = false; + + return ret_val; +} + +#if defined XBEEAPI_CONFIG_ENABLE_DEVELOPER + +#define PRINTABLE_ASCII_FIRST 32U +#define PRINTABLE_ASCII_LAST 126U + +void XBeeDevice::dumpRxBuffer( Stream* p_buf, const bool p_hexView ) +{ + uint8_t c; + while( m_rxBuff.getSize() ) { + if( m_rxBuff.read( &c, 1 ) ) { + if( p_hexView ) { + uint8_t a = '-'; + if(( c>=PRINTABLE_ASCII_FIRST ) && (c<=PRINTABLE_ASCII_LAST)) { + a = c; + } + p_buf->printf("0x%02x (%c) ",c,a); + } else { + p_buf->printf("%c",c); + if( c == '\r' ) { + p_buf->printf("\n"); + } + } + } + } +} + +#endif \ No newline at end of file