Vergil Cola
/
MQTTGatewayK64
Fork of my MQTTGateway
Diff: XbeeMonitor/XBeeLib/XBee/XBee.cpp
- Revision:
- 0:f1d3878b8dd9
diff -r 000000000000 -r f1d3878b8dd9 XbeeMonitor/XBeeLib/XBee/XBee.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/XbeeMonitor/XBeeLib/XBee/XBee.cpp Sat Apr 08 14:45:51 2017 +0000 @@ -0,0 +1,880 @@ +/** + * Copyright (c) 2015 Digi International Inc., + * All rights not expressly granted are reserved. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + * + * Digi International Inc. 11001 Bren Road East, Minnetonka, MN 55343 + * ======================================================================= + */ + +#include "XBeeLib.h" +#include "FrameHandlers/FH_ModemStatus.h" + +/* States for the state machine that processes incoming data on the serial port */ +#define WAITING_FOR_START_FRAME (0) +#define WAITING_FOR_LENGTH_MSB (1) +#define WAITING_FOR_LENGTH_LSB (2) +#define WAITING_FOR_PAYLOAD (3) +#define WAITING_FOR_CHECKSUM (4) + +#define IS_API2() (_mode == ModeAPI2) +#define IS_API_MODE() (_mode == ModeAPI1 || _mode == ModeAPI2) + +using namespace XBeeLib; + +#if defined(FRAME_BUFFER_SIZE_SYNCR) +#if FRAME_BUFFER_SIZE_SYNCR < 2 +#error "FRAME_BUFFER_SIZE_SYNCR must be at least 2" +#endif +#else +#define FRAME_BUFFER_SIZE_SYNCR 1 +#endif + +#define MAX_FRAME_PAYLOAD_LEN_SYNCR (1 /* type */ + 1 /* id */ + 2 /* at cmd*/ + 1 /* status */ + 2 /* MY sender */ + \ + 8 /* 64b sender */ + 20 /* max id */ + 1 /* null ter */ + 2 /* MY parent */ + 1 /* dev type */ + \ + 1 /* source event */ + 2 /* prof. id */ + 2 /* man. id */) + +FrameBuffer XBee::_framebuf_app(FRAME_BUFFER_SIZE, MAX_FRAME_PAYLOAD_LEN); +FrameBuffer XBee::_framebuf_syncr(FRAME_BUFFER_SIZE_SYNCR, MAX_FRAME_PAYLOAD_LEN_SYNCR); + +#if defined(DEVICE_SERIAL_FC) +bool XBee::check_radio_flow_control() +{ + AtCmdFrame::AtCmdResp cmdresp; + uint32_t value; + + if (_serial_flow_type == SerialBase::RTSCTS || _serial_flow_type == SerialBase::CTS) { + cmdresp = get_param("D7", &value); + if (cmdresp != AtCmdFrame::AtCmdRespOk) { + digi_log(LogLevelError, "Could not read CTS configuration. Error %d\r\n", cmdresp); + return false; + } else if (value != 1) { + digi_log(LogLevelError, "Bad CTS configuration. Radio 'D7' param is %d and should be 1\r\n", value); + return false; + } + } + + if (_serial_flow_type == SerialBase::RTSCTS || _serial_flow_type == SerialBase::RTS) { + cmdresp = get_param("D6", &value); + if (cmdresp != AtCmdFrame::AtCmdRespOk) { + digi_log(LogLevelError, "Could not read RTS configuration. Error %d\r\n", cmdresp); + return false; + } else if (value != 1) { + digi_log(LogLevelError, "Bad RTS configuration. Radio 'D6' param is %d and should be 1\r\n", value); + return false; + } + } + + return true; +} +#endif + +/* Class constructor */ +XBee::XBee(PinName tx, PinName rx, PinName reset, PinName rts, PinName cts, int baud) : + _mode(ModeUnknown), _hw_version(0), _fw_version(0), _timeout_ms(SYNC_OPS_TIMEOUT_MS), _dev_addr64(ADDR64_UNASSIGNED), + _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) +{ + + if (reset != NC) { + _reset = new DigitalOut(reset, 1); + } + + _uart = new RawSerial(tx, rx); + _uart->baud(baud); + + _serial_flow_type = SerialBase::Disabled; +#if defined(DEVICE_SERIAL_FC) + if (rts != NC && cts != NC) { + _serial_flow_type = SerialBase::RTSCTS; + _uart->set_flow_control(_serial_flow_type, rts, cts); + } else if (rts != NC && cts == NC) { + _serial_flow_type = SerialBase::RTS; + _uart->set_flow_control(_serial_flow_type, rts); + } else if (rts == NC && cts != NC) { + _serial_flow_type = SerialBase::CTS; + _uart->set_flow_control(_serial_flow_type, cts); + } +#endif + /* Enable the reception of bytes on the serial interface by providing a cb */ + _uart->attach(this, &XBee::uart_read_cb, Serial::RxIrq); + + for (int i = 0; i < MAX_FRAME_HANDLERS; i++) { + _fhandlers[i] = NULL; + } +} + +/* Class destructor */ +XBee::~XBee() +{ + unregister_modem_status_cb(); + + if (_uart != NULL) { + delete _uart; + } + if (_reset != NULL) { + delete _reset; + } +} + +#include <inttypes.h> + +RadioStatus XBee::init(void) +{ + AtCmdFrame::AtCmdResp cmd_resp; + uint32_t var32; + + _initializing = true; + + const unsigned int max_reset_retries = 3; + RadioStatus reset_status; + for (unsigned int i = 0; i < max_reset_retries; i++) { + reset_status = device_reset(); + if (reset_status == Success) { + break; + } + } + if (reset_status != Success) { + return reset_status; + } + + /* Check if radio is in API1 or API2 _mode */ + cmd_resp = get_param("AP", &var32); + if (cmd_resp != AtCmdFrame::AtCmdRespOk) { + return Failure; + } + _mode = (RadioMode)var32; + + /* Read the device unique 64b address */ + uint32_t serialn_high, serialn_low; + cmd_resp = get_param("SH", &serialn_high); + if (cmd_resp != AtCmdFrame::AtCmdRespOk) { + return Failure; + } + + cmd_resp = get_param("SL", &serialn_low); + if (cmd_resp != AtCmdFrame::AtCmdRespOk) { + return Failure; + } + + _dev_addr64 = ((uint64_t)serialn_high << 32) | serialn_low; + + /* Read some important parameters */ + cmd_resp = get_param("HV", &var32); + if (cmd_resp != AtCmdFrame::AtCmdRespOk) { + return Failure; + } + _hw_version = var32; + + cmd_resp = get_param("VR", &var32); + if (cmd_resp != AtCmdFrame::AtCmdRespOk) { + return Failure; + } + _fw_version = var32; + + digi_log(LogLevelInfo, "mode: %02x\r\n", (uint8_t)_mode); + digi_log(LogLevelInfo, "HV: %04x\r\n", _hw_version); + digi_log(LogLevelInfo, "VR: %04x\r\n", _fw_version); + digi_log(LogLevelInfo, "ADDR64: %08x:%08x\r\n", UINT64_HI32(_dev_addr64), UINT64_LO32(_dev_addr64)); + +#if defined(DEVICE_SERIAL_FC) + bool valid_radio_fc = check_radio_flow_control(); + assert(valid_radio_fc == true); +#endif + + _initializing = false; + if (_modem_status_handler != NULL) { + const ApiFrame frame = ApiFrame(ApiFrame::AtModemStatus, (uint8_t *)&_modem_status, sizeof(_modem_status)); + _modem_status_handler->process_frame_data(&frame); + } + + return Success; +} + +uint64_t XBee::get_addr64() const +{ + return _dev_addr64; +} + +RadioStatus XBee::hardware_reset() +{ + if (_reset != NULL) { + volatile uint16_t * const rst_cnt_p = &_hw_reset_cnt; + const uint16_t init_rst_cnt = *rst_cnt_p; + *_reset = 0; + wait_ms(10); + *_reset = 1; + return wait_for_module_to_reset(rst_cnt_p, init_rst_cnt); + } + + return Failure; +} + +RadioStatus XBee::device_reset() +{ + if (hardware_reset() == Success) { + return Success; + } + + return software_reset(); +} + +RadioStatus XBee::wait_for_module_to_reset(volatile uint16_t *rst_cnt_p, uint16_t init_rst_cnt) +{ + Timer timer = Timer(); + timer.start(); + + while (*rst_cnt_p == init_rst_cnt && timer.read_ms() < RESET_TIMEOUT_MS) { + wait_ms(100); + } + + if (*rst_cnt_p == init_rst_cnt) { + digi_log(LogLevelWarning, "Reset Timeout\r\n"); + return Failure; + } + return Success; +} + +/** Callback function called when data is received on the serial port */ +void XBee::uart_read_cb(void) +{ + static uint8_t rxstate = WAITING_FOR_START_FRAME; + static uint16_t framelen = 0; + static uint16_t bytes_read; + static uint8_t chksum; + static ApiFrame *frame = NULL; + static bool last_byte_escaped = false; + static FrameBuffer * framebuf = NULL; + + while (_uart->readable()) { + uint8_t data = _uart->getc(); + + if (IS_API2() && rxstate != WAITING_FOR_START_FRAME) { + if (last_byte_escaped) { + data = data ^ DR_ESCAPE_XOR_BYTE; + last_byte_escaped = false; + } else if (data == DR_ESCAPE_BYTE) { + last_byte_escaped = true; + continue; + } + } + + switch (rxstate) { + case WAITING_FOR_START_FRAME: + if (data == DR_START_OF_FRAME) { + rxstate = WAITING_FOR_LENGTH_MSB; + } + break; + + case WAITING_FOR_LENGTH_MSB: + framelen = data << 8; + rxstate = WAITING_FOR_LENGTH_LSB; + break; + + case WAITING_FOR_LENGTH_LSB: + framelen |= data; + rxstate = WAITING_FOR_PAYLOAD; + bytes_read = 0; + chksum = 0; + /* Sanity check that the frame is smaller than... */ + if (framelen > MAX_FRAME_PAYLOAD_LEN) { + digi_log(LogLevelDebug, "framelen=%d too long\r\n", framelen); + digi_log(LogLevelWarning, "Frame dropped, frame too long. Increase MAX_FRAME_PAYLOAD_LEN define\r\n"); + rxstate = WAITING_FOR_START_FRAME; + } + break; + + case WAITING_FOR_PAYLOAD: + #define CACHED_SIZE 3 + static uint8_t frame_cached[CACHED_SIZE]; + + if (framelen <= CACHED_SIZE) { + if (!bytes_read) { + const ApiFrame::ApiFrameType frame_type = (ApiFrame::ApiFrameType)data; + switch (frame_type) + { + case ApiFrame::AtCmdResp: + case ApiFrame::RemoteCmdResp: + case ApiFrame::TxStatusZBDM: + case ApiFrame::TxStatus: + framebuf = &_framebuf_syncr; + break; + + case ApiFrame::RxPacket64Bit: + case ApiFrame::RxPacket16Bit: + case ApiFrame::Io64Bit: + case ApiFrame::Io16Bit: + case ApiFrame::AtModemStatus: + case ApiFrame::RxPacketAO0: + case ApiFrame::IoSampleRxZBDM: + framebuf = &_framebuf_app; + break; + + case ApiFrame::RxPacketAO1: + case ApiFrame::SensorRxIndAO0: + case ApiFrame::NodeIdentIndAO0: + case ApiFrame::OtaFwUpStatus: + case ApiFrame::RouteRecInd: + case ApiFrame::Many2OneRRInd: + case ApiFrame::TxReq64Bit: + case ApiFrame::TxReq16Bit: + case ApiFrame::AtCmd: + case ApiFrame::AtCmdQueuePV: + case ApiFrame::TxReqZBDM: + case ApiFrame::ExpAddrCmd: + case ApiFrame::RemoteCmdReq: + case ApiFrame::CreateSrcRoute: + case ApiFrame::Invalid: + case ApiFrame::RouteInfo: + case ApiFrame::AggregateAddr: + framebuf = NULL; + break; + } + + if (framebuf == NULL) { + digi_log(LogLevelWarning, "Discarding not supported frame type %02x\r\n", frame_type); + rxstate = WAITING_FOR_START_FRAME; + } else { + frame = framebuf->get_next_free_frame(); + if (frame == NULL) { + /* It's not possible to achive this condition as we discard older frames and only one frame can be used by syncr. commands */ + assert(frame != NULL); + rxstate = WAITING_FOR_START_FRAME; + } else { + frame->set_data_len(framelen - 1); + } + + frame->set_frame_type(frame_type); + } + } else { + frame->set_data(data, bytes_read - 1); + } + chksum += data; + bytes_read++; + if (bytes_read == framelen) { + rxstate = WAITING_FOR_CHECKSUM; + } + break; + } + + + if (bytes_read < CACHED_SIZE) { + frame_cached[bytes_read] = data; + } + else if (bytes_read == CACHED_SIZE) { + const ApiFrame::ApiFrameType frame_type = (ApiFrame::ApiFrameType)frame_cached[0]; + switch (frame_type) + { + case ApiFrame::RemoteCmdResp: + case ApiFrame::TxStatusZBDM: + case ApiFrame::TxStatus: + framebuf = &_framebuf_syncr; + break; + + case ApiFrame::AtCmdResp: + if ((frame_cached[1] != _node_by_ni_frame_id ) && (frame_cached[2] == 'N') && (data == 'D')) + { + framebuf = &_framebuf_app; + } else { + framebuf = &_framebuf_syncr; + } + break; + + case ApiFrame::RxPacket64Bit: + case ApiFrame::RxPacket16Bit: + case ApiFrame::Io64Bit: + case ApiFrame::Io16Bit: + case ApiFrame::AtModemStatus: + case ApiFrame::RxPacketAO0: + case ApiFrame::IoSampleRxZBDM: + framebuf = &_framebuf_app; + break; + + case ApiFrame::RxPacketAO1: + case ApiFrame::SensorRxIndAO0: + case ApiFrame::NodeIdentIndAO0: + case ApiFrame::OtaFwUpStatus: + case ApiFrame::RouteRecInd: + case ApiFrame::Many2OneRRInd: + case ApiFrame::TxReq64Bit: + case ApiFrame::TxReq16Bit: + case ApiFrame::AtCmd: + case ApiFrame::AtCmdQueuePV: + case ApiFrame::TxReqZBDM: + case ApiFrame::ExpAddrCmd: + case ApiFrame::RemoteCmdReq: + case ApiFrame::CreateSrcRoute: + case ApiFrame::Invalid: + case ApiFrame::RouteInfo: + case ApiFrame::AggregateAddr: + framebuf = NULL; + break; + } + + if (framebuf == NULL) { + digi_log(LogLevelWarning, "Discarding not supported frame type %02x\r\n", frame_type); + rxstate = WAITING_FOR_START_FRAME; + } else { + frame = framebuf->get_next_free_frame(); + if (frame == NULL) { + /* It's not possible to achive this condition as we discard older frames and only one frame can be used by syncr. commands */ + assert(frame != NULL); + rxstate = WAITING_FOR_START_FRAME; + } else { + frame->set_data_len(framelen - 1); + } + + frame->set_frame_type(frame_type); + frame->set_data(frame_cached[1], 0); + frame->set_data(frame_cached[2], 1); + frame->set_data(data, 2); + } + } else { + frame->set_data(data, bytes_read - 1); + } + chksum += data; + bytes_read++; + if (bytes_read == framelen) { + rxstate = WAITING_FOR_CHECKSUM; + } + break; + + case WAITING_FOR_CHECKSUM: + chksum += data; + if (chksum == 0xFF) { + /* We got a valid frame!! */ + frame->dump(); + + /* If its a _modem status frame, process it to update the status info of the library. + * The frame is also queued to allow processing it other handlers registered. + * Note that radio_status_update() has to be fast to minimize the impact of processing + * the funcion here */ + if (frame->get_frame_type() == ApiFrame::AtModemStatus) { + radio_status_update((AtCmdFrame::ModemStatus)frame->get_data_at(0)); + if (_initializing) { + framebuf->free_frame(frame); + } else { + framebuf->complete_frame(frame); + } + } else { + framebuf->complete_frame(frame); + /* Note, the frame will be released elsewhere, once it has been processed */ + } + } else { + framebuf->free_frame(frame); + digi_log(LogLevelWarning, "Checksum error, got %02x, %02x\r\n", data, chksum); + } + /* Intentional fall-through */ + default: + rxstate = WAITING_FOR_START_FRAME; + break; + } + } + /* TODO, signal the thread processing incoming frames */ +} + +/* This is a pure virtual function, but exists here because its called from this class to + * to update the status of the object, and can be called before the construction of the + * object has been completed and the virtual functions filled */ +void XBee::radio_status_update(AtCmdFrame::ModemStatus modem_status) +{ + UNUSED_PARAMETER(modem_status); +} + +void XBee::set_timeout(uint16_t timeout_ms) +{ + this->_timeout_ms = timeout_ms; +} + +uint16_t XBee::get_timeout(void) const +{ + return _timeout_ms; +} + +ApiFrame * XBee::get_this_api_frame(uint8_t id, ApiFrame::ApiFrameType type, + ApiFrame::ApiFrameType type2) +{ + Timer timer = Timer(); + timer.start(); + + while (timer.read_ms() < _timeout_ms) { + ApiFrame * frame = _framebuf_syncr.get_next_complete_frame(); + if (frame == NULL) { + wait_ms(10); + continue; + } + + if ((frame->get_frame_type() != type) && + (frame->get_frame_type() != type2)) { + _framebuf_syncr.complete_frame(frame); + wait_ms(1); + continue; + } + + if (frame->get_data_at(ATCMD_RESP_FRAME_ID_OFFSET) != id) { + _framebuf_syncr.complete_frame(frame); + wait_ms(1); + continue; + } + + /* frame found */ + return frame; + } + + digi_log(LogLevelWarning, "Frame type: %02x, id: %02x, timeout\r\n", (uint8_t)type, id); + + return NULL; +} + +void XBee::send_byte_escaping_if(uint8_t data) +{ + if (IS_API2()) { + switch (data) { + case DR_START_OF_FRAME: + case DR_ESCAPE_BYTE: + case DR_XON_BYTE: + case DR_XOFF_BYTE: + _uart->putc(DR_ESCAPE_BYTE); + _uart->putc(data ^ DR_ESCAPE_XOR_BYTE); + break; + default: + _uart->putc(data); + } + } else { + _uart->putc(data); + } +} + +void XBee::send_api_frame(ApiFrame *frame) +{ + uint8_t chksum; + const uint8_t *data; + uint16_t bytes_sent = 0, frame_len; + + frame->dump(); + + frame_len = 1 + frame->get_data_len(); /* frame type + frame payload */ + data = frame->get_data(); + + /* Send the start of frame delimiter */ + _uart->putc(DR_START_OF_FRAME); + + /* Now the length */ + send_byte_escaping_if((uint8_t)(frame_len >> 8)); + send_byte_escaping_if((uint8_t)frame_len); + + /* Send the Frame type and then the payload */ + chksum = (uint8_t)frame->get_frame_type(); + send_byte_escaping_if(chksum); + bytes_sent++; + + /* And now, send the packet payload */ + while (bytes_sent++ < frame_len) { + chksum += *data; + send_byte_escaping_if(*data++); + } + + /* And finally send the checksum */ + send_byte_escaping_if(~chksum); +} + +RadioStatus XBee::register_frame_handler(FrameHandler *const handler) +{ + if (handler != NULL) { + for (int i = 0; i < MAX_FRAME_HANDLERS; i++) { + if (_fhandlers[i] != NULL) { + continue; + } + _fhandlers[i] = handler; + return Success; + } + } + + digi_log(LogLevelError, "No more Frame Handlers available. Increase MAX_FRAME_HANDLERS define\r\n"); + + return Failure; +} + +RadioStatus XBee::unregister_frame_handler(FrameHandler *const handler) +{ + int i; + + if (handler != NULL) { + for (i = 0; i < MAX_FRAME_HANDLERS; i++) { + if (_fhandlers[i] == handler) { + break; + } + } + + if (i == MAX_FRAME_HANDLERS) { + return Failure; + } + + do { + if (i == MAX_FRAME_HANDLERS - 1) { + _fhandlers[i] = NULL; + } else { + _fhandlers[i] = _fhandlers[i + 1]; + } + } while (++i < MAX_FRAME_HANDLERS); + } + + return Success; +} + +XBee::RadioProtocol XBee::get_radio_protocol(void) const +{ + enum HardwareVersion { +#ifdef EXTRA_XBEE_PROTOCOLS + X09_009 = 0x01, + X09_019 = 0x02, + XH9_009 = 0x03, + XH9_019 = 0x04, + X24_009 = 0x05, + X24_019 = 0x06, + X09_001 = 0x07, + XH9_001 = 0x08, + X08_004 = 0x09, + XC09_009 = 0x0A, + XC09_038 = 0x0B, + X24_038 = 0x0C, + X09_009_TX = 0x0D, + X09_019_TX = 0x0E, + XH9_009_TX = 0x0F, + XH9_019_TX = 0x10, + X09_001_TX = 0x11, + XH9_001_TX = 0x12, + XT09B_XXX = 0x13, + XT09_XXX = 0x14, + XC08_009 = 0x15, + XC08_038 = 0x16, +#endif + XB24_AXX_XX = 0x17, + XBP24_AXX_XX = 0x18, + XB24_BXIX_XXX = 0x19, + XBP24_BXIX_XXX = 0x1A, +#ifdef EXTRA_XBEE_PROTOCOLS + XBP09_DXIX_XXX = 0x1B, + XBP09_XCXX_XXX = 0x1C, + XBP08_DXXX_XXX = 0x1D, +#endif + XBP24B = 0x1E, +#ifdef EXTRA_XBEE_PROTOCOLS + XB24_WF = 0x1F, + AMBER_MBUS = 0x20, +#endif + XBP24C = 0x21, + XB24C = 0x22, +#ifdef EXTRA_XBEE_PROTOCOLS + XSC_GEN3 = 0x23, + SRD_868_GEN3 = 0x24, + ABANDONATED = 0x25, + SMT_900LP = 0x26, + WIFI_ATHEROS = 0x27, + SMT_WIFI_ATHEROS = 0x28, + SMT_475LP = 0x29, + XBEE_CELL_TH = 0x2A, + XLR_MODULE = 0x2B, + XB900HP_NZ = 0x2C, + XBP24C_TH_DIP = 0x2D, + XB24C_TH_DIP = 0x2E, + XLR_BASEBOARD = 0x2F, + XBP24C_S2C_SMT = 0x30 +#endif + }; + const bool fw_4_bytes_len = _fw_version > 0x0FFF && _fw_version < 0xFFFF; + const uint8_t fw_nibble_3 = (_fw_version >> (4 * 3)) & 0x000F; + const uint8_t fw_nibble_1 = (_fw_version >> (4 * 1)) & 0x000F; + const uint8_t fw_nibble_0 = (_fw_version >> (4 * 0)) & 0x000F; + const uint8_t hw_version_msb = _hw_version >> 8; + + if (hw_version_msb == XB24_AXX_XX || hw_version_msb == XBP24_AXX_XX) { +#ifdef EXTRA_XBEE_PROTOCOLS + if (fw_4_bytes_len && fw_nibble_3 == 8) { + return DigiMesh; + } + return Raw_802_15_4; +#else + if (!(fw_4_bytes_len && fw_nibble_3 == 8)) { + return Raw_802_15_4; + } +#endif + } else if (hw_version_msb == XB24_BXIX_XXX || hw_version_msb == XBP24_BXIX_XXX) { + if (fw_4_bytes_len && ((fw_nibble_3 == 1 && fw_nibble_1 == 2 && fw_nibble_0 == 0) || fw_nibble_3 == 2)) { + return ZigBee; + } +#ifdef EXTRA_XBEE_PROTOCOLS + if (fw_4_bytes_len && fw_nibble_3 == 3) { + return SmartEnergy; + } + return ZNet; + } else if (hw_version_msb == XBP09_DXIX_XXX) { + if (fw_4_bytes_len && (fw_nibble_3 == 8 || fw_nibble_1 == 8)) { + return DigiMesh; + } + return DigiPoint; + } else if (hw_version_msb == XBP08_DXXX_XXX) { + return DigiPoint; +#endif + } else if (hw_version_msb == XBP24B) { +#ifdef EXTRA_XBEE_PROTOCOLS + if (fw_4_bytes_len && fw_nibble_3 == 3) { + return SmartEnergy; + } + return ZigBee; +#else + if (!(fw_4_bytes_len && fw_nibble_3 == 3)) { + return ZigBee; + } +#endif +#ifdef EXTRA_XBEE_PROTOCOLS + } else if (hw_version_msb == XB24_WF || hw_version_msb == WIFI_ATHEROS || hw_version_msb == SMT_WIFI_ATHEROS) { + return XBeeWiFi; +#endif + } else if (hw_version_msb == XBP24C || hw_version_msb == XB24C) { + if (fw_4_bytes_len && fw_nibble_3 == 2) { + return Raw_802_15_4; + } +#ifdef EXTRA_XBEE_PROTOCOLS + if (fw_4_bytes_len && fw_nibble_3 == 5) { + return SmartEnergy; + } + return ZigBee; +#else + if (!(fw_4_bytes_len && fw_nibble_3 == 5)) { + return ZigBee; + } +#endif +#ifdef EXTRA_XBEE_PROTOCOLS + } else if (hw_version_msb == XSC_GEN3 || hw_version_msb == SRD_868_GEN3) { + if (fw_4_bytes_len && fw_nibble_3 == 8) { + return DigiMesh; + } else if (fw_4_bytes_len && fw_nibble_3 == 1) { + return DigiPoint; + } + return None; + } else if (hw_version_msb == XBEE_CELL_TH) { + return None; + } else if (hw_version_msb == XLR_MODULE) { + return None; + } else if (hw_version_msb == XLR_BASEBOARD) { + return None; + } else if (hw_version_msb == XB900HP_NZ) { + return DigiPoint; + } else if (hw_version_msb == XBP24C_TH_DIP || hw_version_msb == XB24C_TH_DIP || hw_version_msb == XBP24C_S2C_SMT) { + if (fw_4_bytes_len && fw_nibble_3 == 9) { + return DigiMesh; + } + if (fw_4_bytes_len && fw_nibble_3 == 5) { + return SmartEnergy; + } + if (fw_4_bytes_len && fw_nibble_3 == 2) { + return Raw_802_15_4; + } + return ZigBee; + } +#else + } +#endif + + return None; +} + +#define TX_STATUS_OFFSET_ZB 4 +#define TX_STATUS_OFFSET_802 1 + +TxStatus XBee::send_data(ApiFrame *frame) +{ + TxStatus resp = TxStatusTimeout; + ApiFrame *resp_frame; + + send_api_frame(frame); + + /* Wait for the transmit status response packet */ + resp_frame = get_this_api_frame(frame->get_frame_id(), + ApiFrame::TxStatusZBDM, ApiFrame::TxStatus); + if (resp_frame == NULL) { + return resp; + } + + uint8_t index = resp_frame->get_frame_type() == ApiFrame::TxStatusZBDM ? + TX_STATUS_OFFSET_ZB : TX_STATUS_OFFSET_802; + + resp = (TxStatus)resp_frame->get_data_at(index); + + /* Once processed, remove the frame from the buffer */ + _framebuf_syncr.free_frame(resp_frame); + + return resp; +} + +TxStatus XBee::send_data_broadcast(const uint8_t *const data, uint16_t len, bool syncr) +{ + const RemoteXBee remoteDevice = RemoteXBee(ADDR64_BROADCAST); + return send_data(remoteDevice, data, len, syncr); +} + +uint32_t XBee::process_rx_frames() +{ + ApiFrame *frame = NULL; + + while ((frame = _framebuf_app.get_next_complete_frame()) != NULL) { + for (int i = 0; i < MAX_FRAME_HANDLERS; i++) { + + if (_fhandlers[i] == NULL) { + /* No more handlers, break here */ + break; + } + + /* Check if frame and handler match, if not... go for the next one */ + if (frame->get_frame_type() != _fhandlers[i]->get_type()) { + continue; + } + + _fhandlers[i]->process_frame_data(frame); + } + + /* Once processed, remove the frame from the buffer */ + _framebuf_app.free_frame(frame); + } + + const uint32_t dropped_frames = _framebuf_app.get_dropped_frames_count(); + if (dropped_frames != 0) { + digi_log(LogLevelWarning, "process_rx_frames: %d frames dropped!!!\r\n", dropped_frames); + } + + return dropped_frames; +} + +void XBee::register_modem_status_cb(modem_status_cb_t function) +{ + if (_modem_status_handler == NULL) { + _modem_status_handler = new FH_ModemStatus(); + register_frame_handler(_modem_status_handler); + } + _modem_status_handler->register_modem_status_cb(function); +} + +void XBee::unregister_modem_status_cb() +{ + if (_modem_status_handler != NULL) { + _modem_status_handler->unregister_modem_status_cb(); + unregister_frame_handler(_modem_status_handler); + delete _modem_status_handler; + _modem_status_handler = NULL; /* as delete does not set to NULL */ + } +} + +int XBee::get_AI(void) +{ + uint32_t atai; + const AtCmdFrame::AtCmdResp status = get_param("AI", &atai); + + if (status != AtCmdFrame::AtCmdRespOk) { + digi_log(LogLevelError, "get_association_indication() failed with %d\r\n", status); + return -1; + } + return atai; +}