Renan Alves / XBeeLib

Dependents:   Navitec-Firmware

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers XBee.cpp Source File

XBee.cpp

00001 /**
00002  * Copyright (c) 2015 Digi International Inc.,
00003  * All rights not expressly granted are reserved.
00004  *
00005  * This Source Code Form is subject to the terms of the Mozilla Public
00006  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
00007  * You can obtain one at http://mozilla.org/MPL/2.0/.
00008  *
00009  * Digi International Inc. 11001 Bren Road East, Minnetonka, MN 55343
00010  * =======================================================================
00011  */
00012 
00013 #include "XBeeLib.h"
00014 #include "FrameHandlers/FH_ModemStatus.h"
00015 
00016 /* States for the state machine that processes incoming data on the serial port */
00017 #define WAITING_FOR_START_FRAME (0)
00018 #define WAITING_FOR_LENGTH_MSB  (1)
00019 #define WAITING_FOR_LENGTH_LSB  (2)
00020 #define WAITING_FOR_PAYLOAD     (3)
00021 #define WAITING_FOR_CHECKSUM    (4)
00022 
00023 #define IS_API2()               (_mode == ModeAPI2)
00024 #define IS_API_MODE()           (_mode == ModeAPI1 || _mode == ModeAPI2)
00025 
00026 using namespace XBeeLib;
00027 
00028 #if defined(FRAME_BUFFER_SIZE_SYNCR)
00029 #if FRAME_BUFFER_SIZE_SYNCR < 2
00030 #error "FRAME_BUFFER_SIZE_SYNCR must be at least 2"
00031 #endif
00032 #else
00033 #define FRAME_BUFFER_SIZE_SYNCR     1
00034 #endif
00035 
00036 #define MAX_FRAME_PAYLOAD_LEN_SYNCR (1  /* type */         + 1  /* id */       + 2 /* at cmd*/    + 1 /* status */    + 2 /* MY sender */ + \
00037                                      8  /* 64b sender */   + 20 /* max id */   + 1 /* null ter */ + 2 /* MY parent */ + 1  /* dev type */ + \
00038                                      1  /* source event */ + 2  /* prof. id */ + 2 /* man. id */)
00039 
00040 FrameBuffer XBee::_framebuf_app(FRAME_BUFFER_SIZE, MAX_FRAME_PAYLOAD_LEN);
00041 FrameBuffer XBee::_framebuf_syncr(FRAME_BUFFER_SIZE_SYNCR, MAX_FRAME_PAYLOAD_LEN_SYNCR);
00042 
00043 #if defined(DEVICE_SERIAL_FC)
00044 bool XBee::check_radio_flow_control()
00045 {
00046     AtCmdFrame::AtCmdResp cmdresp;
00047     uint32_t value;
00048 
00049     if (_serial_flow_type == SerialBase::RTSCTS || _serial_flow_type == SerialBase::CTS) {
00050         cmdresp = get_param("D7", &value);
00051         if (cmdresp != AtCmdFrame::AtCmdRespOk) {
00052             digi_log(LogLevelError, "Could not read CTS configuration. Error %d\r\n", cmdresp);
00053             return false;
00054         } else if (value != 1) {
00055             digi_log(LogLevelError, "Bad CTS configuration. Radio 'D7' param is %d and should be 1\r\n", value);
00056             return false;
00057         }
00058     }
00059 
00060     if (_serial_flow_type == SerialBase::RTSCTS || _serial_flow_type == SerialBase::RTS) {
00061         cmdresp = get_param("D6", &value);
00062         if (cmdresp != AtCmdFrame::AtCmdRespOk) {
00063             digi_log(LogLevelError, "Could not read RTS configuration. Error %d\r\n", cmdresp);
00064             return false;
00065         } else if (value != 1) {
00066             digi_log(LogLevelError, "Bad RTS configuration. Radio 'D6' param is %d and should be 1\r\n", value);
00067             return false;
00068         }
00069     }
00070 
00071     return true;
00072 }
00073 #endif
00074 
00075 /* Class constructor */
00076 XBee::XBee(PinName tx, PinName rx, PinName reset, PinName rts, PinName cts, int baud) :
00077     _mode(ModeUnknown), _hw_version(0), _fw_version(0), _timeout_ms(SYNC_OPS_TIMEOUT_MS), _dev_addr64(ADDR64_UNASSIGNED),
00078     _reset(NULL), _tx_options(0), _hw_reset_cnt(0), _wd_reset_cnt(0), _modem_status_handler(NULL), _modem_status(AtCmdFrame::HwReset), _initializing(true), _node_by_ni_frame_id(0)
00079 {
00080 
00081     if (reset != NC) {
00082         _reset = new DigitalOut(reset, 1);
00083     }
00084 
00085     _uart = new UnbufferedSerial(tx, rx);
00086     _uart->baud(baud);
00087 
00088     _serial_flow_type = SerialBase::Disabled;
00089 #if defined(DEVICE_SERIAL_FC)
00090     if (rts != NC && cts != NC) {
00091         _serial_flow_type = SerialBase::RTSCTS;
00092         _uart->set_flow_control(_serial_flow_type, rts, cts);
00093     } else if (rts != NC && cts == NC) {
00094         _serial_flow_type = SerialBase::RTS;
00095         _uart->set_flow_control(_serial_flow_type, rts);
00096     } else if (rts == NC && cts != NC) {
00097         _serial_flow_type = SerialBase::CTS;
00098         _uart->set_flow_control(_serial_flow_type, cts);
00099     }
00100 #endif
00101     /* Enable the reception of bytes on the serial interface by providing a cb */
00102     //_uart->attach(this, &XBee::uart_read_cb, Serial::RxIrq);
00103     _uart->attach(callback(this,&XBee::uart_read_cb), SerialBase::RxIrq);
00104 
00105     for (int i = 0; i < MAX_FRAME_HANDLERS; i++) {
00106         _fhandlers[i] = NULL;
00107     }
00108 }
00109 
00110 /* Class destructor */
00111 XBee::~XBee()
00112 {
00113     unregister_modem_status_cb();
00114 
00115     if (_uart != NULL) {
00116         delete _uart;
00117     }
00118     if (_reset != NULL) {
00119         delete _reset;
00120     }
00121 }
00122 
00123 #include <inttypes.h>
00124 
00125 RadioStatus XBee::init(void)
00126 {
00127     AtCmdFrame::AtCmdResp cmd_resp;
00128     uint32_t var32;
00129 
00130     _initializing = true;
00131 
00132     const unsigned int max_reset_retries = 3;
00133     RadioStatus reset_status;
00134     for (unsigned int i = 0; i < max_reset_retries; i++) {
00135         reset_status = device_reset();
00136         if (reset_status == Success) {
00137             break;
00138         }
00139     }
00140     if (reset_status != Success) {
00141         return reset_status;
00142     }
00143 
00144     /* Check if radio is in API1 or API2 _mode */
00145     cmd_resp = get_param("AP", &var32);
00146     if (cmd_resp != AtCmdFrame::AtCmdRespOk) {
00147         return Failure;
00148     }
00149     _mode = (RadioMode)var32;
00150 
00151     /* Read the device unique 64b address */
00152     uint32_t serialn_high, serialn_low;
00153     cmd_resp = get_param("SH", &serialn_high);
00154     if (cmd_resp != AtCmdFrame::AtCmdRespOk) {
00155         return Failure;
00156     }
00157 
00158     cmd_resp = get_param("SL", &serialn_low);
00159     if (cmd_resp != AtCmdFrame::AtCmdRespOk) {
00160         return Failure;
00161     }
00162 
00163     _dev_addr64 = ((uint64_t)serialn_high << 32) | serialn_low;
00164 
00165     /* Read some important parameters */
00166     cmd_resp = get_param("HV", &var32);
00167     if (cmd_resp != AtCmdFrame::AtCmdRespOk) {
00168         return Failure;
00169     }
00170     _hw_version = var32;
00171 
00172     cmd_resp = get_param("VR", &var32);
00173     if (cmd_resp != AtCmdFrame::AtCmdRespOk) {
00174         return Failure;
00175     }
00176     _fw_version = var32;
00177 
00178     digi_log(LogLevelInfo, "mode:   %02x\r\n", (uint8_t)_mode);
00179     digi_log(LogLevelInfo, "HV:     %04x\r\n", _hw_version);
00180     digi_log(LogLevelInfo, "VR:     %04x\r\n", _fw_version);
00181     digi_log(LogLevelInfo, "ADDR64: %08x:%08x\r\n", UINT64_HI32(_dev_addr64), UINT64_LO32(_dev_addr64));
00182 
00183 #if defined(DEVICE_SERIAL_FC)
00184     bool valid_radio_fc = check_radio_flow_control();
00185     assert(valid_radio_fc == true);
00186 #endif
00187 
00188     _initializing = false;
00189     if (_modem_status_handler != NULL) {
00190         const ApiFrame frame = ApiFrame(ApiFrame::AtModemStatus, (uint8_t *)&_modem_status, sizeof(_modem_status));
00191         _modem_status_handler->process_frame_data(&frame);
00192     }
00193 
00194     return Success;
00195 }
00196 
00197 uint64_t XBee::get_addr64() const
00198 {
00199     return _dev_addr64;
00200 }
00201 
00202 RadioStatus XBee::hardware_reset()
00203 {
00204     if (_reset != NULL) {
00205         volatile uint16_t * const rst_cnt_p = &_hw_reset_cnt;
00206         const uint16_t init_rst_cnt = *rst_cnt_p;
00207         *_reset = 0;
00208         //wait_ms(10);
00209         ThisThread::sleep_for(chrono::milliseconds(10));
00210         *_reset = 1;
00211         return wait_for_module_to_reset(rst_cnt_p, init_rst_cnt);
00212     }
00213 
00214     return Failure;
00215 }
00216 
00217 RadioStatus XBee::device_reset()
00218 {
00219     if (hardware_reset() == Success) {
00220         return Success;
00221     }
00222 
00223     return software_reset();
00224 }
00225 
00226 RadioStatus XBee::wait_for_module_to_reset(volatile uint16_t *rst_cnt_p, uint16_t init_rst_cnt)
00227 {
00228     Timer timer = Timer();
00229     timer.start();
00230 
00231     //while (*rst_cnt_p == init_rst_cnt && timer.read_ms() < RESET_TIMEOUT_MS) {
00232     //wait_ms(100);
00233     while (*rst_cnt_p == init_rst_cnt && timer.elapsed_time().count() < RESET_TIMEOUT_MS) {
00234      
00235         ThisThread::sleep_for(chrono::milliseconds(100));
00236     }
00237 
00238     if (*rst_cnt_p == init_rst_cnt) {
00239         digi_log(LogLevelWarning, "Reset Timeout\r\n");
00240         return Failure;
00241     }
00242     return Success;
00243 }
00244 
00245 /** Callback function called when data is received on the serial port */
00246 void XBee::uart_read_cb(void)
00247 {
00248     static uint8_t rxstate = WAITING_FOR_START_FRAME;
00249     static uint16_t framelen = 0;
00250     static uint16_t bytes_read;
00251     static uint8_t chksum;
00252     static ApiFrame *frame = NULL;
00253     static bool last_byte_escaped = false;
00254     static FrameBuffer * framebuf = NULL;
00255 
00256     while (_uart->readable()) {
00257         //uint8_t data = _uart->getc();
00258         uint8_t data;
00259         _uart->read(&data, 1);
00260 
00261         if (IS_API2() && rxstate != WAITING_FOR_START_FRAME) {
00262             if (last_byte_escaped) {
00263                 data = data ^ DR_ESCAPE_XOR_BYTE;
00264                 last_byte_escaped = false;
00265             } else if (data == DR_ESCAPE_BYTE) {
00266                 last_byte_escaped = true;
00267                 continue;
00268             }
00269         }
00270 
00271         switch (rxstate) {
00272             case WAITING_FOR_START_FRAME:
00273                 if (data == DR_START_OF_FRAME) {
00274                     rxstate = WAITING_FOR_LENGTH_MSB;
00275                 }
00276                 break;
00277 
00278             case WAITING_FOR_LENGTH_MSB:
00279                 framelen = data << 8;
00280                 rxstate = WAITING_FOR_LENGTH_LSB;
00281                 break;
00282 
00283             case WAITING_FOR_LENGTH_LSB:
00284                 framelen |= data;
00285                 rxstate = WAITING_FOR_PAYLOAD;
00286                 bytes_read = 0;
00287                 chksum = 0;
00288                 /* Sanity check that the frame is smaller than... */
00289                 if (framelen > MAX_FRAME_PAYLOAD_LEN) {
00290                     digi_log(LogLevelDebug, "framelen=%d too long\r\n", framelen);
00291                     digi_log(LogLevelWarning, "Frame dropped, frame too long. Increase MAX_FRAME_PAYLOAD_LEN define\r\n");
00292                     rxstate = WAITING_FOR_START_FRAME;
00293                 }
00294                 break;
00295 
00296             case WAITING_FOR_PAYLOAD:
00297                 #define CACHED_SIZE 3
00298                 static uint8_t frame_cached[CACHED_SIZE];
00299 
00300                 if (framelen <= CACHED_SIZE) {
00301                     if (!bytes_read) {
00302                         const ApiFrame::ApiFrameType frame_type = (ApiFrame::ApiFrameType)data;
00303                         switch (frame_type)
00304                         {
00305                             case ApiFrame::AtCmdResp:
00306                             case ApiFrame::RemoteCmdResp:
00307                             case ApiFrame::TxStatusZBDM:
00308                             case ApiFrame::TxStatus:
00309                                 framebuf = &_framebuf_syncr;
00310                                 break;
00311 
00312                             case ApiFrame::RxPacket64Bit:
00313                             case ApiFrame::RxPacket16Bit:
00314                             case ApiFrame::Io64Bit:
00315                             case ApiFrame::Io16Bit:
00316                             case ApiFrame::AtModemStatus:
00317                             case ApiFrame::RxPacketAO0:
00318                             case ApiFrame::IoSampleRxZBDM:
00319                                 framebuf = &_framebuf_app;
00320                                 break;
00321 
00322                             case ApiFrame::RxPacketAO1:
00323                             case ApiFrame::SensorRxIndAO0:
00324                             case ApiFrame::NodeIdentIndAO0:
00325                             case ApiFrame::OtaFwUpStatus:
00326                             case ApiFrame::RouteRecInd:
00327                             case ApiFrame::Many2OneRRInd:
00328                             case ApiFrame::TxReq64Bit:
00329                             case ApiFrame::TxReq16Bit:
00330                             case ApiFrame::AtCmd:
00331                             case ApiFrame::AtCmdQueuePV:
00332                             case ApiFrame::TxReqZBDM:
00333                             case ApiFrame::ExpAddrCmd:
00334                             case ApiFrame::RemoteCmdReq:
00335                             case ApiFrame::CreateSrcRoute:
00336                             case ApiFrame::Invalid:
00337                             case ApiFrame::RouteInfo:
00338                             case ApiFrame::AggregateAddr:
00339                                 framebuf = NULL;
00340                                 break;
00341                         }
00342 
00343                         if (framebuf == NULL) {
00344                             digi_log(LogLevelWarning, "Discarding not supported frame type %02x\r\n", frame_type);
00345                             rxstate = WAITING_FOR_START_FRAME;
00346                         } else {
00347                             frame = framebuf->get_next_free_frame();
00348                             if (frame == NULL) {
00349                                 /* It's not possible to achive this condition as we discard older frames and only one frame can be used by syncr. commands */
00350                                 assert(frame != NULL);
00351                                 rxstate = WAITING_FOR_START_FRAME;
00352                             } else {
00353                                 frame->set_data_len(framelen - 1);
00354                             }
00355 
00356                             frame->set_frame_type(frame_type);
00357                         }
00358                     } else {
00359                         frame->set_data(data, bytes_read - 1);
00360                     }
00361                     chksum += data;
00362                     bytes_read++;
00363                     if (bytes_read == framelen) {
00364                         rxstate = WAITING_FOR_CHECKSUM;
00365                     }
00366                     break;
00367                 }
00368 
00369 
00370                 if (bytes_read < CACHED_SIZE) {
00371                     frame_cached[bytes_read] = data;
00372                 }
00373                 else if (bytes_read == CACHED_SIZE) {
00374                     const ApiFrame::ApiFrameType frame_type = (ApiFrame::ApiFrameType)frame_cached[0];
00375                     switch (frame_type)
00376                     {
00377                         case ApiFrame::RemoteCmdResp:
00378                         case ApiFrame::TxStatusZBDM:
00379                         case ApiFrame::TxStatus:
00380                             framebuf = &_framebuf_syncr;
00381                             break;
00382 
00383                         case ApiFrame::AtCmdResp:
00384                             if ((frame_cached[1] != _node_by_ni_frame_id ) && (frame_cached[2] == 'N') && (data == 'D'))
00385                             {
00386                                 framebuf = &_framebuf_app;
00387                             } else {
00388                                 framebuf = &_framebuf_syncr;
00389                             }
00390                             break;
00391 
00392                         case ApiFrame::RxPacket64Bit:
00393                         case ApiFrame::RxPacket16Bit:
00394                         case ApiFrame::Io64Bit:
00395                         case ApiFrame::Io16Bit:
00396                         case ApiFrame::AtModemStatus:
00397                         case ApiFrame::RxPacketAO0:
00398                         case ApiFrame::IoSampleRxZBDM:
00399                             framebuf = &_framebuf_app;
00400                             break;
00401 
00402                         case ApiFrame::RxPacketAO1:
00403                         case ApiFrame::SensorRxIndAO0:
00404                         case ApiFrame::NodeIdentIndAO0:
00405                         case ApiFrame::OtaFwUpStatus:
00406                         case ApiFrame::RouteRecInd:
00407                         case ApiFrame::Many2OneRRInd:
00408                         case ApiFrame::TxReq64Bit:
00409                         case ApiFrame::TxReq16Bit:
00410                         case ApiFrame::AtCmd:
00411                         case ApiFrame::AtCmdQueuePV:
00412                         case ApiFrame::TxReqZBDM:
00413                         case ApiFrame::ExpAddrCmd:
00414                         case ApiFrame::RemoteCmdReq:
00415                         case ApiFrame::CreateSrcRoute:
00416                         case ApiFrame::Invalid:
00417                         case ApiFrame::RouteInfo:
00418                         case ApiFrame::AggregateAddr:
00419                             framebuf = NULL;
00420                             break;
00421                     }
00422 
00423                     if (framebuf == NULL) {
00424                         digi_log(LogLevelWarning, "Discarding not supported frame type %02x\r\n", frame_type);
00425                         rxstate = WAITING_FOR_START_FRAME;
00426                     } else {
00427                         frame = framebuf->get_next_free_frame();
00428                         if (frame == NULL) {
00429                             /* It's not possible to achive this condition as we discard older frames and only one frame can be used by syncr. commands */
00430                             assert(frame != NULL);
00431                             rxstate = WAITING_FOR_START_FRAME;
00432                         } else {
00433                             frame->set_data_len(framelen - 1);
00434                         }
00435 
00436                         frame->set_frame_type(frame_type);
00437                         frame->set_data(frame_cached[1], 0);
00438                         frame->set_data(frame_cached[2], 1);
00439                         frame->set_data(data, 2);
00440                     }
00441                 } else {
00442                     frame->set_data(data, bytes_read - 1);
00443                 }
00444                 chksum += data;
00445                 bytes_read++;
00446                 if (bytes_read == framelen) {
00447                     rxstate = WAITING_FOR_CHECKSUM;
00448                 }
00449                 break;
00450 
00451             case WAITING_FOR_CHECKSUM:
00452                 chksum += data;
00453                 if (chksum == 0xFF) {
00454                     /* We got a valid frame!! */
00455                     frame->dump();
00456 
00457                     /* If its a _modem status frame, process it to update the status info of the library.
00458                      * The frame is also queued to allow processing it other handlers registered.
00459                      * Note that radio_status_update() has to be fast to minimize the impact of processing
00460                      * the funcion here */
00461                     if (frame->get_frame_type() == ApiFrame::AtModemStatus) {
00462                         radio_status_update((AtCmdFrame::ModemStatus)frame->get_data_at(0));
00463                         if (_initializing) {
00464                             framebuf->free_frame(frame);
00465                         } else {
00466                             framebuf->complete_frame(frame);
00467                         }
00468                     } else {
00469                         framebuf->complete_frame(frame);
00470                         /* Note, the frame will be released elsewhere, once it has been processed */
00471                     }
00472                 } else {
00473                     framebuf->free_frame(frame);
00474                     digi_log(LogLevelWarning, "Checksum error, got %02x, %02x\r\n", data, chksum);
00475                 }
00476                 /* Intentional fall-through */
00477             default:
00478                 rxstate = WAITING_FOR_START_FRAME;
00479                 break;
00480         }
00481     }
00482     /* TODO, signal the thread processing incoming frames */
00483 }
00484 
00485 /* This is a pure virtual function, but exists here because its called from this class to
00486  * to update the status of the object, and can be called before the construction of the
00487  * object has been completed and the virtual functions filled */
00488 void XBee::radio_status_update(AtCmdFrame::ModemStatus modem_status)
00489 {
00490     UNUSED_PARAMETER(modem_status);
00491 }
00492 
00493 void XBee::set_timeout(uint16_t timeout_ms)
00494 {
00495     this->_timeout_ms = timeout_ms;
00496 }
00497 
00498 uint16_t XBee::get_timeout(void) const
00499 {
00500     return _timeout_ms;
00501 }
00502 
00503 ApiFrame * XBee::get_this_api_frame(uint8_t id, ApiFrame::ApiFrameType type,
00504                                           ApiFrame::ApiFrameType type2)
00505 {
00506     Timer timer = Timer();
00507     timer.start();
00508 
00509     //while (timer.read_ms() < _timeout_ms) {
00510     while (timer.elapsed_time().count() < _timeout_ms) {
00511         
00512         ApiFrame * frame = _framebuf_syncr.get_next_complete_frame();
00513         if (frame == NULL) {
00514             //wait_ms(10);
00515             ThisThread::sleep_for(chrono::milliseconds(10));
00516             continue;
00517         }
00518 
00519         if ((frame->get_frame_type() != type) &&
00520             (frame->get_frame_type() != type2)) {
00521             _framebuf_syncr.complete_frame(frame);
00522             //wait_ms(1);
00523             ThisThread::sleep_for(chrono::milliseconds(1));
00524             continue;
00525         }
00526 
00527         if (frame->get_data_at(ATCMD_RESP_FRAME_ID_OFFSET) != id) {
00528             _framebuf_syncr.complete_frame(frame);
00529             //wait_ms(1);
00530             ThisThread::sleep_for(chrono::milliseconds(1));
00531             continue;
00532         }
00533 
00534         /* frame found */
00535         return frame;
00536     }
00537 
00538     digi_log(LogLevelWarning, "Frame type: %02x, id: %02x, timeout\r\n", (uint8_t)type, id);
00539 
00540     return NULL;
00541 }
00542 
00543 void XBee::send_byte_escaping_if(uint8_t data)
00544 {
00545     if (IS_API2()) {
00546         switch (data) {
00547             case DR_START_OF_FRAME:
00548             case DR_ESCAPE_BYTE:
00549             case DR_XON_BYTE:
00550             case DR_XOFF_BYTE:
00551                 //_uart->putc(DR_ESCAPE_BYTE);
00552                 //_uart->putc(data ^ DR_ESCAPE_XOR_BYTE);
00553                 _uart->write((uint8_t *)DR_ESCAPE_BYTE, 1);
00554                 _uart->write((uint8_t *)(data ^ DR_ESCAPE_XOR_BYTE),1);
00555                 break;
00556             default:
00557                 //_uart->putc(data);
00558                _uart->write(&data,1);
00559         }
00560     } else {
00561         //_uart->putc(data);
00562         _uart->write(&data,1);
00563     }
00564 }
00565 
00566 void XBee::send_api_frame(ApiFrame *frame)
00567 {
00568     uint8_t chksum;
00569     const uint8_t *data;
00570     uint16_t bytes_sent = 0, frame_len;
00571 
00572     frame->dump();
00573 
00574     frame_len = 1 + frame->get_data_len(); /* frame type + frame payload */
00575     data = frame->get_data();
00576 
00577     /* Send the start of frame delimiter */
00578     //_uart->putc(DR_START_OF_FRAME);
00579     _uart->write((uint8_t *)DR_START_OF_FRAME,1);
00580 
00581     /* Now the length */
00582     send_byte_escaping_if((uint8_t)(frame_len >> 8));
00583     send_byte_escaping_if((uint8_t)frame_len);
00584 
00585     /* Send the Frame type and then the payload */
00586     chksum = (uint8_t)frame->get_frame_type();
00587     send_byte_escaping_if(chksum);
00588     bytes_sent++;
00589 
00590     /* And now, send the packet payload */
00591     while (bytes_sent++ < frame_len) {
00592         chksum += *data;
00593         send_byte_escaping_if(*data++);
00594     }
00595 
00596     /* And finally send the checksum */
00597     send_byte_escaping_if(~chksum);
00598 }
00599 
00600 RadioStatus XBee::register_frame_handler(FrameHandler *const handler)
00601 {
00602     if (handler != NULL) {
00603         for (int i = 0; i < MAX_FRAME_HANDLERS; i++) {
00604             if (_fhandlers[i] != NULL) {
00605                 continue;
00606             }
00607             _fhandlers[i] = handler;
00608             return Success;
00609         }
00610     }
00611 
00612     digi_log(LogLevelError, "No more Frame Handlers available. Increase MAX_FRAME_HANDLERS define\r\n");
00613 
00614     return Failure;
00615 }
00616 
00617 RadioStatus XBee::unregister_frame_handler(FrameHandler *const handler)
00618 {
00619     int i;
00620 
00621     if (handler != NULL) {
00622         for (i = 0; i < MAX_FRAME_HANDLERS; i++) {
00623             if (_fhandlers[i] == handler) {
00624                 break;
00625             }
00626         }
00627 
00628         if (i == MAX_FRAME_HANDLERS) {
00629             return Failure;
00630         }
00631 
00632         do {
00633             if (i == MAX_FRAME_HANDLERS - 1) {
00634                 _fhandlers[i] = NULL;
00635             } else {
00636                 _fhandlers[i] = _fhandlers[i + 1];
00637             }
00638         } while (++i < MAX_FRAME_HANDLERS);
00639     }
00640 
00641     return Success;
00642 }
00643 
00644 XBee::RadioProtocol XBee::get_radio_protocol(void) const
00645 {
00646     enum HardwareVersion {
00647 #ifdef EXTRA_XBEE_PROTOCOLS
00648         X09_009 = 0x01,
00649         X09_019 = 0x02,
00650         XH9_009 = 0x03,
00651         XH9_019 = 0x04,
00652         X24_009 = 0x05,
00653         X24_019 = 0x06,
00654         X09_001 = 0x07,
00655         XH9_001 = 0x08,
00656         X08_004 = 0x09,
00657         XC09_009 = 0x0A,
00658         XC09_038 = 0x0B,
00659         X24_038 = 0x0C,
00660         X09_009_TX = 0x0D,
00661         X09_019_TX = 0x0E,
00662         XH9_009_TX = 0x0F,
00663         XH9_019_TX = 0x10,
00664         X09_001_TX = 0x11,
00665         XH9_001_TX = 0x12,
00666         XT09B_XXX = 0x13,
00667         XT09_XXX = 0x14,
00668         XC08_009 = 0x15,
00669         XC08_038 = 0x16,
00670 #endif
00671         XB24_AXX_XX = 0x17,
00672         XBP24_AXX_XX = 0x18,
00673         XB24_BXIX_XXX = 0x19,
00674         XBP24_BXIX_XXX = 0x1A,
00675 #ifdef EXTRA_XBEE_PROTOCOLS
00676         XBP09_DXIX_XXX = 0x1B,
00677         XBP09_XCXX_XXX = 0x1C,
00678         XBP08_DXXX_XXX = 0x1D,
00679 #endif
00680         XBP24B = 0x1E,
00681 #ifdef EXTRA_XBEE_PROTOCOLS
00682         XB24_WF = 0x1F,
00683         AMBER_MBUS = 0x20,
00684 #endif
00685         XBP24C = 0x21,
00686         XB24C = 0x22,
00687 #ifdef EXTRA_XBEE_PROTOCOLS
00688         XSC_GEN3 = 0x23,
00689         SRD_868_GEN3 = 0x24,
00690         ABANDONATED = 0x25,
00691         SMT_900LP = 0x26,
00692         WIFI_ATHEROS = 0x27,
00693         SMT_WIFI_ATHEROS = 0x28,
00694         SMT_475LP = 0x29,
00695         XBEE_CELL_TH = 0x2A,
00696         XLR_MODULE = 0x2B,
00697         XB900HP_NZ = 0x2C,
00698         XBP24C_TH_DIP = 0x2D,
00699         XB24C_TH_DIP = 0x2E,
00700         XLR_BASEBOARD = 0x2F,
00701         XBP24C_S2C_SMT = 0x30
00702 #endif
00703     };
00704     const bool fw_4_bytes_len = _fw_version > 0x0FFF && _fw_version < 0xFFFF;
00705     const uint8_t fw_nibble_3 = (_fw_version >> (4 * 3)) & 0x000F;
00706     const uint8_t fw_nibble_1 = (_fw_version >> (4 * 1)) & 0x000F;
00707     const uint8_t fw_nibble_0 = (_fw_version >> (4 * 0)) & 0x000F;
00708     const uint8_t hw_version_msb = _hw_version >> 8;
00709 
00710     if (hw_version_msb == XB24_AXX_XX || hw_version_msb == XBP24_AXX_XX) {
00711 #ifdef EXTRA_XBEE_PROTOCOLS
00712         if (fw_4_bytes_len && fw_nibble_3 == 8) {
00713             return DigiMesh;
00714         }
00715         return Raw_802_15_4;
00716 #else
00717         if (!(fw_4_bytes_len && fw_nibble_3 == 8)) {
00718             return Raw_802_15_4;
00719         }
00720 #endif
00721     } else if (hw_version_msb == XB24_BXIX_XXX || hw_version_msb == XBP24_BXIX_XXX) {
00722         if (fw_4_bytes_len && ((fw_nibble_3 == 1 && fw_nibble_1 == 2 && fw_nibble_0 == 0) || fw_nibble_3 == 2)) {
00723             return ZigBee;
00724         }
00725 #ifdef EXTRA_XBEE_PROTOCOLS
00726         if (fw_4_bytes_len && fw_nibble_3 == 3) {
00727             return SmartEnergy;
00728         }
00729         return ZNet;
00730     } else if (hw_version_msb == XBP09_DXIX_XXX) {
00731         if (fw_4_bytes_len && (fw_nibble_3 == 8 || fw_nibble_1 == 8))  {
00732             return DigiMesh;
00733         }
00734         return DigiPoint;
00735     } else if (hw_version_msb == XBP08_DXXX_XXX) {
00736         return DigiPoint;
00737 #endif
00738     } else if (hw_version_msb == XBP24B) {
00739 #ifdef EXTRA_XBEE_PROTOCOLS
00740         if (fw_4_bytes_len && fw_nibble_3 == 3) {
00741             return SmartEnergy;
00742         }
00743         return ZigBee;
00744 #else
00745         if (!(fw_4_bytes_len && fw_nibble_3 == 3)) {
00746             return ZigBee;
00747         }
00748 #endif
00749 #ifdef EXTRA_XBEE_PROTOCOLS
00750     } else if (hw_version_msb == XB24_WF || hw_version_msb == WIFI_ATHEROS || hw_version_msb == SMT_WIFI_ATHEROS) {
00751         return XBeeWiFi;
00752 #endif
00753     } else if (hw_version_msb == XBP24C || hw_version_msb == XB24C) {
00754         if (fw_4_bytes_len && fw_nibble_3 == 2) {
00755             return Raw_802_15_4;
00756         }
00757 #ifdef EXTRA_XBEE_PROTOCOLS
00758         if (fw_4_bytes_len && fw_nibble_3 == 5) {
00759             return SmartEnergy;
00760         }
00761         return ZigBee;
00762 #else
00763         if (!(fw_4_bytes_len && fw_nibble_3 == 5)) {
00764             return ZigBee;
00765         }
00766 #endif
00767 #ifdef EXTRA_XBEE_PROTOCOLS
00768     } else if (hw_version_msb == XSC_GEN3 || hw_version_msb == SRD_868_GEN3) {
00769         if (fw_4_bytes_len && fw_nibble_3 == 8) {
00770             return DigiMesh;
00771         } else if (fw_4_bytes_len && fw_nibble_3 == 1) {
00772             return DigiPoint;
00773         }
00774         return None;
00775     } else if (hw_version_msb == XBEE_CELL_TH) {
00776         return None;
00777     } else if (hw_version_msb == XLR_MODULE) {
00778         return None;
00779     } else if (hw_version_msb == XLR_BASEBOARD) {
00780         return None;
00781     } else if (hw_version_msb == XB900HP_NZ) {
00782         return DigiPoint;
00783     } else if (hw_version_msb == XBP24C_TH_DIP || hw_version_msb == XB24C_TH_DIP || hw_version_msb == XBP24C_S2C_SMT) {
00784         if (fw_4_bytes_len && fw_nibble_3 == 9) {
00785             return DigiMesh;
00786         }
00787         if (fw_4_bytes_len && fw_nibble_3 == 5) {
00788             return SmartEnergy;
00789         }
00790         if (fw_4_bytes_len && fw_nibble_3 == 2) {
00791             return Raw_802_15_4;
00792         }
00793         return ZigBee;
00794     }
00795 #else
00796     }
00797 #endif
00798 
00799     return None;
00800 }
00801 
00802 #define TX_STATUS_OFFSET_ZB     4
00803 #define TX_STATUS_OFFSET_802    1
00804 
00805 TxStatus XBee::send_data(ApiFrame *frame)
00806 {
00807     TxStatus resp = TxStatusTimeout;
00808     ApiFrame *resp_frame;
00809 
00810     send_api_frame(frame);
00811 
00812     /* Wait for the transmit status response packet */
00813     resp_frame = get_this_api_frame(frame->get_frame_id(),
00814                     ApiFrame::TxStatusZBDM, ApiFrame::TxStatus);
00815     if (resp_frame == NULL) {
00816         return resp;
00817     }
00818 
00819     uint8_t index = resp_frame->get_frame_type() == ApiFrame::TxStatusZBDM ?
00820             TX_STATUS_OFFSET_ZB : TX_STATUS_OFFSET_802;
00821 
00822     resp = (TxStatus)resp_frame->get_data_at(index);
00823 
00824     /* Once processed, remove the frame from the buffer */
00825     _framebuf_syncr.free_frame(resp_frame);
00826 
00827     return resp;
00828 }
00829 
00830 TxStatus XBee::send_data_broadcast(const uint8_t *const data, uint16_t len, bool syncr)
00831 {
00832     const RemoteXBee remoteDevice = RemoteXBee(ADDR64_BROADCAST);
00833     return send_data(remoteDevice, data, len, syncr);
00834 }
00835 
00836 uint32_t XBee::process_rx_frames()
00837 {
00838     ApiFrame *frame = NULL;
00839 
00840     while ((frame = _framebuf_app.get_next_complete_frame()) != NULL) {
00841         for (int i = 0; i < MAX_FRAME_HANDLERS; i++) {
00842 
00843             if (_fhandlers[i] == NULL) {
00844                 /* No more handlers, break here */
00845                 break;
00846             }
00847 
00848             /* Check if frame and handler match, if not... go for the next one */
00849             if (frame->get_frame_type() != _fhandlers[i]->get_type()) {
00850                 continue;
00851             }
00852 
00853             _fhandlers[i]->process_frame_data(frame);
00854         }
00855 
00856         /* Once processed, remove the frame from the buffer */
00857         _framebuf_app.free_frame(frame);
00858     }
00859 
00860     const uint32_t dropped_frames = _framebuf_app.get_dropped_frames_count();
00861     if (dropped_frames != 0) {
00862         digi_log(LogLevelWarning, "process_rx_frames: %d frames dropped!!!\r\n", dropped_frames);
00863     }
00864 
00865     return dropped_frames;
00866 }
00867 
00868 void XBee::register_modem_status_cb(modem_status_cb_t function)
00869 {
00870     if (_modem_status_handler == NULL) {
00871         _modem_status_handler = new FH_ModemStatus();
00872         register_frame_handler(_modem_status_handler);
00873     }
00874     _modem_status_handler->register_modem_status_cb(function);
00875 }
00876 
00877 void XBee::unregister_modem_status_cb()
00878 {
00879     if (_modem_status_handler != NULL) {
00880         _modem_status_handler->unregister_modem_status_cb();
00881         unregister_frame_handler(_modem_status_handler);
00882         delete _modem_status_handler;
00883         _modem_status_handler = NULL; /* as delete does not set to NULL */
00884     }
00885 }
00886 
00887 int XBee::get_AI(void)
00888 {
00889     uint32_t atai;
00890     const AtCmdFrame::AtCmdResp status = get_param("AI", &atai);
00891 
00892     if (status != AtCmdFrame::AtCmdRespOk) {
00893         digi_log(LogLevelError, "get_association_indication() failed with %d\r\n", status);
00894         return -1;
00895     }
00896     return atai;
00897 }