AT command firmware for MultiTech Dot devices.

Fork of mDot_AT_firmware by MultiTech

Dot Library Not Included!

Because these example programs can be used for both mDot and xDot devices, the LoRa stack is not included. The libmDot library should be imported if building for mDot devices. The libxDot library should be imported if building for xDot devices. The AT firmware was last tested with mbed-os-5.4.7. Using a version past mbed-os-5.4.7 will cause the build to fail. The library used with the AT firmware has to match the mbed-os version.

Dot Library Version 3 Updates

Dot Library versions 3.x.x require a channel plan to be injected into the stack. The Dot-Examples and Dot-AT-Firmware do this by defining a macro called "CHANNEL_PLAN" that controls the channel plan that will be used in the examples. Available channel plans will be in the Dot Library repository in the plans folder.

Revision 20 and earlier of Dot-Examples and revision 15 and earlier of Dot-AT-Firmware should be used with Dot Library versions prior to 3.0.0.

Fota Library

Th Fota Library must be added to compile for mDot 3.1.0 with Fota support. Latest dev libraries and 3.2.0 release will include Fota with libmDot/libxDot.

AT Firmware Description

This AT Firmware is what ships on mDot and xDot devices. It provides an AT command interface for using the mDot or xDot for LoRa communication.

AT command documentation can be found on Multitech.com.

The firmware changelog can be found here. The library changelog can be found here.

Dot Libraries

Dot Library Limitations

The commit messages in libmDot-mbed5 and libmDot-dev-mbed5 specify the version of the Dot library the commit contains and the version of mbed-os it was compiled against. We recommend building your application with the version of mbed-os specified in the commit message of the version of the Dot library you're using. This will ensure that you don't run into any runtime issues caused by differences in the mbed-os versions.

Stable and development libraries are available for both mDot and xDot platforms. The library chosen must match the target platform. Compiling for the mDot platform with the xDot library or vice versa will not succeed.

mDot Library

Development library for mDot.

libmDot-dev

Stable library for mDot.

libmDot

xDot Library

Development library for xDot.

libxDot-dev

Stable library for xDot.

libxDot

Revision:
14:f9a77400b622
Parent:
11:05e0f30c03a6
Child:
16:d5cf2af81a6d
--- a/CommandTerminal/CommandTerminal.cpp	Fri Nov 04 19:10:24 2016 +0000
+++ b/CommandTerminal/CommandTerminal.cpp	Fri Nov 04 14:25:43 2016 -0500
@@ -4,8 +4,15 @@
 #include "MTSLog.h"
 #include <cstdarg>
 #include <deque>
+#if defined(TARGET_XDOT_L151CC)
+#include "xdot_low_power.h"
+#endif
 
+#if defined(TARGET_MTS_MDOT_F411RE)
 const char CommandTerminal::banner[] = "\r\n\nMultiTech Systems LoRa XBee Module\r\n\n";
+#else
+const char CommandTerminal::banner[] = "\r\n\nMultiTech Systems LoRa xDot Module\r\n\n";
+#endif
 const char CommandTerminal::helpline[] = "Enter '?' for help\r\n";
 
 const char CommandTerminal::newline[] = "\r\n";
@@ -25,120 +32,372 @@
 const char CommandTerminal::escape_sequence[] = "+++";
 
 mts::ATSerial* CommandTerminal::_serialp = NULL;
+mDot* CommandTerminal::_dot = NULL;
+
+CommandTerminal::RadioEvent* CommandTerminal::_events = new RadioEvent();
 
 static bool serial_data_mode = false;
 static bool peer_to_peer = false;
 
-void CommandTerminal::addCommand(Command* cmd) {
-    _commands.push_back(cmd);
+std::string CommandTerminal::_errorMessage = "";
+
+void CommandTerminal::setErrorMessage(const char* message) {
+    _errorMessage.assign(message);
+}
+
+void CommandTerminal::setErrorMessage(const std::string& message) {
+    _errorMessage.assign(message);
 }
 
-CommandTerminal::CommandTerminal(mts::ATSerial& serial, mDot* dot)
-:
-  _serial(serial),
-  _dot(dot),
-  _events(new RadioEvent(serial)),
-  _mode(mDot::COMMAND_MODE),
-  _idle_thread(idle, NULL, osPriorityLow),
-  _sleep_standby(true),
-  _xbee_on_sleep(XBEE_ON_SLEEP) {
+const Command CommandTerminal::_commands[NO_OF_COMMANDS] = {
+    CmdAttention(),
+    CmdIdentification(),
+    CmdResetCpu(),
+    CmdDummy("Enable/Disable Echo", "ATE", "ATE0: disable, ATE1: enable", "(0,1)"),
+    CmdDummy("Enable/Disable Verbose", "ATV", "ATV0: disable, ATV1: enable", "(0,1)"),
+    CmdDummy("Hardware Flow Control", "AT&K", "AT&K0: disable, AT&K3: enable", "(0,3)"),
+
+    CmdFactoryDefault(),
+    CmdSaveConfig(),
+    CmdDisplayConfig(),
+    CmdDisplayStats(),
+    CmdResetStats(),
+    CmdSerialBaudRate(),
+    CmdDebugBaudRate(),
+    CmdStartUpMode(),
+
+    CmdFrequencyBand(),
+    CmdFrequencySubBand(),
+    CmdPublicNetwork(),
+    CmdDeviceId(),
+    CmdDeviceClass(),
+
+    CmdAppPort(),
+    CmdNetworkAddress(),
+    CmdNetworkSessionKey(),
+    CmdDataSessionKey(),
+    CmdUplinkCounter(),
+    CmdDownlinkCounter(),
+    CmdSaveSession(),
+    CmdRestoreSession(),
+    CmdNetworkKey(),
+    CmdNetworkId(),
 
-    _dot->setEvents(_events);
-    _dot->setWakeupCallback(this, &CommandTerminal::wakeup);
-    _serialp = &serial;
+    CmdJoinDelay(),
+// Remove join settings commands until valid case for changing default settings
+//    CmdJoinRx1Offset(),
+//    CmdJoinRx2Datarate(),
+//    CmdJoinRx2Frequency(),
+    CmdJoinRequest(),
+    CmdJoinRetries(),
+    CmdJoinByteOrder(),
+    CmdNetworkJoinMode(),
+    CmdPreserveSession(),
+    CmdNetworkJoinStatus(),
+    CmdNetworkLinkCheck(),
+    CmdLinkCheckCount(),
+    CmdLinkCheckThreshold(),
+    CmdEncryption(),
+
+    CmdRssi(),
+    CmdSnr(),
+    CmdDataPending(),
+
+    CmdSessionDataRate(),
+    CmdChannelMask(),
 
-    addCommand(new CmdAttention(_dot));
-    addCommand(new CmdIdentification(_dot, serial));
-    addCommand(new CmdResetCpu(_dot, serial));
-    addCommand(new CmdDummy(_dot, "Enable/Disable Echo", "ATE", "ATE0: disable, ATE1: enable"));
-    addCommand(new CmdDummy(_dot, "Enable/Disable Verbose", "ATV", "ATV0: disable, ATV1: enable"));
-    addCommand(new CmdDummy(_dot, "Hardware Flow Control", "AT&K", "AT&K0: disable, AT&K3: enable"));
+    CmdTxDataRate(),
+    CmdTxPower(),
+    CmdAntennaGain(),
+    CmdTxFrequency(),
+    CmdTxInverted(),
+    CmdTxWait(),
+    CmdTxChannel(),
+    CmdTxNextMs(),
+    CmdTimeOnAir(),
+
+    CmdRxDelay(),
+    CmdRxOutput(),
+    CmdRxInverted(),
+
+    CmdErrorCorrection(),
+    CmdCRC(),
+    CmdAdaptiveDataRate(),
+
+    CmdACKAttempts(),
+    CmdRepeat(),
+    CmdMacCmd(),
+
+    CmdSendString(),
+    CmdSendBinary(),
+    CmdReceiveOnce(),
 
-    addCommand(new CmdFactoryDefault(_dot));
-    addCommand(new CmdSaveConfig(_dot));
-    addCommand(new CmdDisplayConfig(_dot, serial));
-    addCommand(new CmdDisplayStats(_dot, serial));
-    addCommand(new CmdResetStats(_dot, serial));
-    addCommand(new CmdSerialBaudRate(_dot, serial));
-    addCommand(new CmdDebugBaudRate(_dot, serial));
-    addCommand(new CmdStartUpMode(_dot, serial));
+    CmdDummy("Serial Data Mode", "AT+SD", "Enter serial data mode, exit with '+++'", "NONE"),
+    CmdDummy("Sleep Mode", "AT+SLEEP", "Enter sleep mode (0:deepsleep,1:sleep)", "(0,1)"),
+    CmdSerialClearOnError(),
+    CmdWakeMode(),
+    CmdWakeInterval(),
+    CmdWakePin(),
+    CmdWakeDelay(),
+    CmdWakeTimeout(),
+    CmdPing(),
+    CmdLogLevel(),
+
+    CmdDummy("***** Test Commands *****", "", "", ""),
+    CmdRxDataRate(),
+    CmdRxFrequency(),
+    CmdReceiveContinuous(),
+    CmdSendStringOnInterval(),
+
+#ifdef MTS_RADIO_DEBUG_COMMANDS
+    CmdDummy("***** Debug Commands *****", "", "", ""),
+    CmdSendContinuous(),
+    CmdWriteProtectedConfig(),
+    CmdDumpRegisters(),
+    CmdEraseFlash(),
+    CmdDisableDutyCycle(),
+#endif
+
+};
+
+const verify_ptr_t CommandTerminal::_verify[NO_OF_COMMANDS] = {
+    CmdDummy::verify,
+    CmdDummy::verify,
+    CmdDummy::verify,
+    CmdDummy::verify,
+    CmdDummy::verify,
+    CmdDummy::verify,
 
-    addCommand(new CmdFrequencyBand(_dot, serial));
-    addCommand(new CmdFrequencySubBand(_dot, serial));
-    addCommand(new CmdPublicNetwork(_dot, serial));
-    addCommand(new CmdDeviceId(_dot, serial));
-    addCommand(new CmdDeviceClass(_dot, serial));
+    CmdDummy::verify,
+    CmdDummy::verify,
+    CmdDummy::verify,
+    CmdDummy::verify,
+    CmdDummy::verify,
+    CmdSerialBaudRate::verify,
+    CmdDebugBaudRate::verify,
+    CmdStartUpMode::verify,
+
+    CmdFrequencyBand::verify,
+    CmdFrequencySubBand::verify,
+    CmdPublicNetwork::verify,
+    CmdDeviceId::verify,
+    CmdDeviceClass::verify,
 
-    addCommand(new CmdAppPort(_dot, serial));
-    addCommand(new CmdNetworkAddress(_dot, serial));
-    addCommand(new CmdNetworkSessionKey(_dot, serial));
-    addCommand(new CmdDataSessionKey(_dot, serial));
-    addCommand(new CmdUplinkCounter(_dot, serial));
-    addCommand(new CmdDownlinkCounter(_dot, serial));
-    addCommand(new CmdSaveSession(_dot, serial));
-    addCommand(new CmdRestoreSession(_dot, serial));
-    addCommand(new CmdNetworkKey(_dot, serial));
-    addCommand(new CmdNetworkId(_dot, serial));
+    CmdAppPort::verify,
+    CmdNetworkAddress::verify,
+    CmdNetworkSessionKey::verify,
+    CmdDataSessionKey::verify,
+    CmdUplinkCounter::verify,
+    CmdDownlinkCounter::verify,
+    CmdDummy::verify,
+    CmdDummy::verify,
+    CmdNetworkKey::verify,
+    CmdNetworkId::verify,
+
+    CmdJoinDelay::verify,
+// Remove join settings commands until valid case for changing default settings
+//    CmdJoinRx1Offset::verify,
+//    CmdJoinRx2Datarate::verify,
+//    CmdJoinRx2Frequency::verify,
+    CmdJoinRequest::verify,
+    CmdJoinRetries::verify,
+    CmdJoinByteOrder::verify,
+    CmdNetworkJoinMode::verify,
+    CmdPreserveSession::verify,
+    CmdDummy::verify,
+    CmdDummy::verify,
+    CmdLinkCheckCount::verify,
+    CmdLinkCheckThreshold::verify,
+    CmdEncryption::verify,
+
+    CmdDummy::verify,
+    CmdDummy::verify,
+    CmdDummy::verify,
+
+    CmdSessionDataRate::verify,
+    CmdChannelMask::verify,
 
-    addCommand(new CmdJoinDelay(_dot, serial));
-    addCommand(new CmdJoinRequest(_dot, serial));
-    addCommand(new CmdJoinRetries(_dot, serial));
-    addCommand(new CmdJoinByteOrder(_dot, serial));
-    addCommand(new CmdNetworkJoinMode(_dot, serial));
-    addCommand(new CmdPreserveSession(_dot, serial));
-    addCommand(new CmdNetworkJoinStatus(_dot, serial));
-    addCommand(new CmdNetworkLinkCheck(_dot, serial));
-    addCommand(new CmdLinkCheckCount(_dot, serial));
-    addCommand(new CmdLinkCheckThreshold(_dot, serial));
-    addCommand(new CmdEncryption(_dot, serial));
+    CmdTxDataRate::verify,
+    CmdTxPower::verify,
+    CmdAntennaGain::verify,
+    CmdTxFrequency::verify,
+    CmdTxInverted::verify,
+    CmdTxWait::verify,
+    CmdTxChannel::verify,
+    CmdTxNextMs::verify,
+    CmdTimeOnAir::verify,
+
+    CmdRxDelay::verify,
+    CmdRxOutput::verify,
+    CmdRxInverted::verify,
+
+    CmdErrorCorrection::verify,
+    CmdCRC::verify,
+    CmdAdaptiveDataRate::verify,
+
+    CmdACKAttempts::verify,
+    CmdRepeat::verify,
+    CmdMacCmd::verify,
+
+    CmdSendString::verify,
+    CmdSendBinary::verify,
+    CmdReceiveOnce::verify,
+
+    CmdDummy::verify,
+    CmdDummy::verify,
+    CmdSerialClearOnError::verify,
+    CmdWakeMode::verify,
+    CmdWakeInterval::verify,
+    CmdWakePin::verify,
+    CmdWakeDelay::verify,
+    CmdWakeTimeout::verify,
+    CmdDummy::verify,
+    CmdLogLevel::verify,
+
+    CmdDummy::verify,
+    CmdRxDataRate::verify,
+    CmdRxFrequency::verify,
+    CmdReceiveContinuous::verify,
+    CmdSendStringOnInterval::verify,
 
-    addCommand(new CmdRssi(_dot, serial));
-    addCommand(new CmdSnr(_dot, serial));
-    addCommand(new CmdDataPending(_dot, serial));
+#ifdef MTS_RADIO_DEBUG_COMMANDS
+    CmdDummy::verify,
+    CmdSendContinuous::verify,
+    CmdDummy::verify,
+    CmdDummy::verify,
+    CmdEraseFlash::verify,
+    CmdDisableDutyCycle::verify,
+#endif
 
-    addCommand(new CmdSessionDataRate(_dot, serial));
+};
+
+const action_ptr_t CommandTerminal::_action[NO_OF_COMMANDS] = {
+    CmdAttention::action,
+    CmdIdentification::action,
+    CmdResetCpu::action,
+    CmdDummy::action,
+    CmdDummy::action,
+    CmdDummy::action,
 
-    addCommand(new CmdTxDataRate(_dot, serial));
-    addCommand(new CmdTxPower(_dot, serial));
-    addCommand(new CmdAntennaGain(_dot, serial));
-    addCommand(new CmdTxFrequency(_dot, serial));
-    addCommand(new CmdTxInverted(_dot, serial));
-    addCommand(new CmdTxWait(_dot, serial));
-    addCommand(new CmdTxChannel(_dot, serial));
-    addCommand(new CmdTxNextMs(_dot, serial));
-    addCommand(new CmdTimeOnAir(_dot, serial));
+    CmdFactoryDefault::action,
+    CmdSaveConfig::action,
+    CmdDisplayConfig::action,
+    CmdDisplayStats::action,
+    CmdResetStats::action,
+    CmdSerialBaudRate::action,
+    CmdDebugBaudRate::action,
+    CmdStartUpMode::action,
+
+    CmdFrequencyBand::action,
+    CmdFrequencySubBand::action,
+    CmdPublicNetwork::action,
+    CmdDeviceId::action,
+    CmdDeviceClass::action,
+
+    CmdAppPort::action,
+    CmdNetworkAddress::action,
+    CmdNetworkSessionKey::action,
+    CmdDataSessionKey::action,
+    CmdUplinkCounter::action,
+    CmdDownlinkCounter::action,
+    CmdSaveSession::action,
+    CmdRestoreSession::action,
+    CmdNetworkKey::action,
+    CmdNetworkId::action,
 
-    addCommand(new CmdRxDelay(_dot, serial));
-    addCommand(new CmdRxOutput(_dot, serial));
-    addCommand(new CmdRxInverted(_dot, serial));
+    CmdJoinDelay::action,
+// Remove join settings commands until valid case for changing default settings
+//    CmdJoinRx1Offset::action,
+//    CmdJoinRx2Datarate::action,
+//    CmdJoinRx2Frequency::action,
+    CmdJoinRequest::action,
+    CmdJoinRetries::action,
+    CmdJoinByteOrder::action,
+    CmdNetworkJoinMode::action,
+    CmdPreserveSession::action,
+    CmdNetworkJoinStatus::action,
+    CmdNetworkLinkCheck::action,
+    CmdLinkCheckCount::action,
+    CmdLinkCheckThreshold::action,
+    CmdEncryption::action,
+
+    CmdRssi::action,
+    CmdSnr::action,
+    CmdDataPending::action,
 
-    addCommand(new CmdErrorCorrection(_dot, serial));
-    addCommand(new CmdCRC(_dot, serial));
-    addCommand(new CmdAdaptiveDataRate(_dot, serial));
+    CmdSessionDataRate::action,
+    CmdChannelMask::action,
 
-    addCommand(new CmdACKAttempts(_dot, serial));
-    addCommand(new CmdRepeat(_dot, serial));
+    CmdTxDataRate::action,
+    CmdTxPower::action,
+    CmdAntennaGain::action,
+    CmdTxFrequency::action,
+    CmdTxInverted::action,
+    CmdTxWait::action,
+    CmdTxChannel::action,
+    CmdTxNextMs::action,
+    CmdTimeOnAir::action,
 
-    addCommand(new CmdSendString(_dot, serial));
-    addCommand(new CmdSendBinary(_dot, serial));
-    addCommand(new CmdReceiveOnce(_dot, serial));
+    CmdRxDelay::action,
+    CmdRxOutput::action,
+    CmdRxInverted::action,
+
+    CmdErrorCorrection::action,
+    CmdCRC::action,
+    CmdAdaptiveDataRate::action,
+
+    CmdACKAttempts::action,
+    CmdRepeat::action,
+    CmdMacCmd::action,
 
-    addCommand(new CmdDummy(_dot, "Serial Data Mode", "AT+SD", "Enter serial data mode, exit with '+++'"));
-    addCommand(new CmdDummy(_dot, "Sleep Mode", "AT+SLEEP", "Enter sleep mode (0:deepsleep,1:sleep)"));
-    addCommand(new CmdSerialClearOnError(_dot, serial));
-    addCommand(new CmdWakeMode(_dot, serial));
-    addCommand(new CmdWakeInterval(_dot, serial));
-    addCommand(new CmdWakePin(_dot, serial));
-    addCommand(new CmdWakeDelay(_dot, serial));
-    addCommand(new CmdWakeTimeout(_dot, serial));
-    addCommand(new CmdPing(_dot, serial));
-    addCommand(new CmdLogLevel(_dot, serial));
+    CmdSendString::action,
+    CmdSendBinary::action,
+    CmdReceiveOnce::action,
+
+    CmdDummy::action,
+    CmdDummy::action,
+    CmdSerialClearOnError::action,
+    CmdWakeMode::action,
+    CmdWakeInterval::action,
+    CmdWakePin::action,
+    CmdWakeDelay::action,
+    CmdWakeTimeout::action,
+    CmdPing::action,
+    CmdLogLevel::action,
+
+    CmdDummy::action,
+    CmdRxDataRate::action,
+    CmdRxFrequency::action,
+    CmdReceiveContinuous::action,
+    CmdSendStringOnInterval::action,
 
-    addCommand(new CmdDummy(_dot, "***** Test Commands *****", "", ""));
-    addCommand(new CmdRxDataRate(_dot, serial));
-    addCommand(new CmdRxFrequency(_dot, serial));
-    addCommand(new CmdReceiveContinuous(_dot, serial));
-    addCommand(new CmdSendStringOnInterval(_dot, serial));
+#ifdef MTS_RADIO_DEBUG_COMMANDS
+    CmdDummy::action,
+    CmdSendContinuous::action,
+    CmdWriteProtectedConfig::action,
+    CmdDumpRegisters::action,
+    CmdEraseFlash::action,
+    CmdDisableDutyCycle::action,
+#endif
+
+};
+
+CommandTerminal::CommandTerminal(mts::ATSerial& serial) :
+  _serial(serial),
+  _mode(mDot::COMMAND_MODE),
+  _sleep_standby(true),
+#if defined(TARGET_MTS_MDOT_F411RE)
+  _xbee_on_sleep(XBEE_ON_SLEEP),
+#else
+  _xbee_on_sleep(GPIO2),
+#endif
+  _autoOTAEnabled(false)
+{
+    _serialp = &serial;
+}
+
+void CommandTerminal::init() {
+    _dot->setEvents(_events);
 }
 
 void CommandTerminal::printHelp() {
@@ -160,10 +419,11 @@
     write(header.c_str());
     write(newline);
     write(newline);
-    for (std::vector<Command*>::iterator it = _commands.begin(); it != _commands.end(); ++it) {
-        name = (*it)->name();
-        text = (*it)->text();
-        desc = (*it)->desc();
+
+    for (int i = 0; i < NO_OF_COMMANDS; i++) {
+        name = _commands[i].name();
+        text = _commands[i].text();
+        desc = _commands[i].desc();
         write(text);
         if (strlen(text) < 8)
             write(tab);
@@ -182,23 +442,23 @@
 }
 
 bool CommandTerminal::writeable() {
-    return _serial.writeable();
+    return _serialp->writeable();
 }
 
 bool CommandTerminal::readable() {
-    return _serial.readable();
+    return _serialp->readable();
 }
 
 char CommandTerminal::read() {
     char ch;
-    _serial.read(&ch, 1);
+    _serialp->read(&ch, 1);
     return ch;
 }
 
 void CommandTerminal::write(const char* message) {
     while (!writeable())
         ;
-    _serial.write(message, strlen(message));
+    _serialp->write(message, strlen(message));
 }
 
 void CommandTerminal::writef(const char* format, ...) {
@@ -209,7 +469,7 @@
     int size = vsnprintf(buff, 256, format, ap);
     while (!writeable())
         ;
-    _serial.write(buff, size);
+    _serialp->write(buff, size);
     va_end(ap);
 }
 
@@ -227,12 +487,17 @@
         timeout = _dot->getWakeDelay();
 
         // wait for timeout or start of serial data
-        while (!readable() && serial_read_timer.read_ms() < timeout && !_serial.escaped()) {
+        while (!readable() && serial_read_timer.read_ms() < timeout && !_serialp->escaped()) {
             osDelay(2);
         }
     }
 
-    if (readable() && !_serial.escaped()) {
+    if (!readable() && _events->SendAck()) {
+        logDebug("SERIAL NOT READABLE and ACK REQUESTED");
+        goto L_SEND;
+    }
+
+    if (readable() && !_serialp->escaped()) {
 
         serial_read_timer.reset();
         timeout = _dot->getWakeTimeout();
@@ -242,7 +507,7 @@
                 serial_buffer.push_back(read());
                 serial_read_timer.reset();
 
-                if (_serial.escaped())
+                if (_serialp->escaped())
                     break;
             }
         }
@@ -251,14 +516,15 @@
 
         if (!serial_buffer.empty()) {
             if (_dot->getStartUpMode() == mDot::SERIAL_MODE)
-                _xbee_on_sleep = GPIO_PIN_RESET;
+                 _xbee_on_sleep = GPIO_PIN_RESET;
 
+L_SEND:
             // wait for any duty cycle limit to expire
-            while (_dot->getNextTxMs() > 0 && !_serial.escaped()) {
+            while (_dot->getNextTxMs() > 0 && !_serialp->escaped()) {
                 osDelay(10);
             }
 
-            if (!_dot->getIsTransmitting()) {
+            if (_dot->getIsIdle()) {
                 logDebug("Received serial data, sending out radio.");
 
                 if (_dot->send(serial_buffer, false) != mDot::MDOT_OK) {
@@ -270,15 +536,28 @@
                 } else {
 
                     // wait for send to finish
-                    while (_dot->getIsTransmitting() && !_serial.escaped())
+                    while (!_dot->getIsIdle() && !_serialp->escaped()) {
                         osDelay(10);
+                    }
 
                     // call recv to wait for any packet before sending again
-                    if (!_serial.escaped())
+                    if (!_serialp->escaped())
                         _dot->recv(data);
 
                     // Clear the serial buffer if send is success
                     serial_buffer.clear();
+
+                    // In class C mode pending data will be sent automatically without uplink
+                    if (_dot->getClass() != "C") {
+                        if (_dot->getDataPending()) {
+                            logDebug("Data is pending");
+                            goto L_SEND;
+                        }
+                        if (_dot->getAckRequested()) {
+                            logDebug("Ack requested");
+                            goto L_SEND;
+                        }
+                    }
                 }
             } else {
                 logDebug("Radio is busy, cannot send.\r\n");
@@ -290,14 +569,13 @@
         }
     }
 
-    if (!_serial.readable() && _dot->getStartUpMode() == mDot::SERIAL_MODE && !_serial.escaped()) {
-        _xbee_on_sleep = GPIO_PIN_RESET;
-        sleep(_sleep_standby);
+    if (!_serialp->readable() && _dot->getStartUpMode() == mDot::SERIAL_MODE && !_serialp->escaped()) {
+        sleep(true);
     }
 
-    if (_serial.escaped()) {
-        _serial.clearEscaped();
-        _serial.rxClear();
+    if (_serialp->escaped()) {
+        _serialp->clearEscaped();
+        _serialp->rxClear();
         serial_data_mode = false;
         _mode = mDot::COMMAND_MODE;
         logDebug("Exit Serial Mode");
@@ -323,42 +601,46 @@
     int cnt = 0;
 
     while (!_dot->getNetworkJoinStatus()) {
-        write("\r\nJoining network... ");
+        if (!serial_data_mode) {
+            write("\r\nJoining network... ");
+        }
+        logInfo("Joining network... ");
 
         if (_dot->getNextTxMs() > 0) {
-            int rand_time = rand() % 10000;
-            writef("\r\nWaiting %lu s before next join attempt\r\n", (_dot->getNextTxMs() + rand_time) / 1000);
+            if (!serial_data_mode) {
+                writef("\r\nWaiting %lu s before next join attempt\r\n", _dot->getNextTxMs() / 1000);
+            }
+            logInfo("Waiting %lu s before next join attempt", _dot->getNextTxMs() / 1000);
 
             tmr.reset();
             while (_dot->getNextTxMs() > 0 && !_serial.escaped()) {
-                osDelay(2);
+                osDelay(20);
             }
-
-            tmr.reset();
-            while (tmr.read_ms() < rand_time && !_serial.escaped())
-                osDelay(10);
         }
 
         if (!_serial.escaped() && _dot->joinNetworkOnce() == mDot::MDOT_OK) {
-            write("Network Joined\r\n");
-            write(done);
+            if (!serial_data_mode) {
+                write("Network Joined\r\n");
+                write(done);
+            }
+            logInfo("Network Joined");
             return false;
         }
 
-        write("Network Join failed\r\n");
-        write(error);
+        if (!serial_data_mode) {
+            write("Network Join failed\r\n");
+            write(error);
+        }
+        logInfo("Network Join failed");
 
-        if (!_serial.escaped() && _dot->getJoinRetries() > 0 && cnt++ > _dot->getJoinRetries()) {
+        if (!_serial.escaped() && _dot->getFrequencySubBand() != 0 && _dot->getJoinRetries() > 0 && cnt++ > _dot->getJoinRetries()) {
             cnt = 0;
 
-            if (_dot->getFrequencyBand() == mDot::FB_915) {
+            if (_dot->getFrequencyBand() == mDot::FB_US915 || _dot->getFrequencyBand() == mDot::FB_AU915 ) {
                 uint8_t band = ((_dot->getFrequencySubBand()) % 8) + 1;
                 logWarning("Join retries exhausted, switching to sub band %u", band);
                 _dot->setFrequencySubBand(band);
             }
-
-            if (sleep < 60 * 60 * 1000)
-                sleep *= 2;
         }
 
         tmr.reset();
@@ -370,8 +652,11 @@
             _serial.clearEscaped();
             serial_data_mode = false;
             _mode = mDot::COMMAND_MODE;
-            write("Join Canceled\r\n");
-            write(done);
+            if (!serial_data_mode) {
+                write("Join Canceled\r\n");
+                write(done);
+            }
+            logInfo("Join Canceled\r\n");
             return true;
         }
     }
@@ -385,11 +670,15 @@
     bool running = true;
     bool echo = _dot->getEcho();
     std::string command;
+#if defined(TARGET_MTS_MDOT_F411RE)
     std::deque<std::string> history;
     int history_index = -1;
+#endif
     std::vector<std::string> args;
     bool join_canceled = false;
 
+    _autoOTAEnabled = _dot->getJoinMode() == mDot::AUTO_OTA;
+
     if (_dot->getStartUpMode() == mDot::SERIAL_MODE) {
 
         serial_data_mode = true;
@@ -450,20 +739,18 @@
         peer_to_peer = false;
     }
 
-
-
     //Run terminal session
     while (running) {
 
         // wait for input to reduce at command idle current
         while (!readable() || _mode == mDot::SERIAL_MODE) {
-            if (!join_canceled && _dot->getJoinMode() == mDot::AUTO_OTA) {
+            if (!join_canceled && _autoOTAEnabled) {
                 join_canceled = autoJoinCheck();
                 if (join_canceled)
                     command.clear();
             }
 
-            if (_dot->getJoinMode() != mDot::AUTO_OTA || (!join_canceled && _dot->getJoinMode() == mDot::AUTO_OTA)) {
+            if (!_autoOTAEnabled || (!join_canceled && _autoOTAEnabled)) {
                 switch (_mode) {
                     case mDot::SERIAL_MODE:
                         // signal wakeup, read serial and output to radio
@@ -501,6 +788,7 @@
                     if (readable())
                         ch2 = read();
 
+#if defined(TARGET_MTS_MDOT_F411RE)
                     if (ch1 == 0x5b && ch2 == 0x41) {
                         // up key
                         for (size_t i = 0; i < command.size() + 1; i++) {
@@ -530,6 +818,7 @@
                             writef("%s", history[history_index].c_str());
                         }
                     }
+#endif
                 }
                 while (readable())
                     read();
@@ -639,9 +928,20 @@
                     write(done);
                 } else {
                     _sleep_standby = !(args.size() > 1 && args[1] == "1");
+#if defined(TARGET_MTS_MDOT_F411RE)
+                    //Read the board ID. If all 0's, it is revision B. This hardware does not support deep sleep.
+                    DigitalIn ID2(PC_4);
+                    DigitalIn ID1(PC_5);
+                    DigitalIn ID0(PD_2);
+                    if(ID2 == 0 && ID1 == 0 && ID0 == 0 && _sleep_standby == 1){
+                        _sleep_standby = 0;
+                        logWarning("This hardware version does not support deep sleep. Using sleep mode instead.");
+                    }
+#endif                    
                     write(done);
+                    osDelay(5);
                     this->sleep(_sleep_standby);
-                    wait(0.1);
+                    osDelay(1);
                 }
             }
         } else {
@@ -661,28 +961,27 @@
             }
 
             // search for command
-            for (std::vector<Command*>::iterator it = _commands.begin(); it != _commands.end() && !found; ++it) {
-                Command* cmd = *it;
+            for (int i = 0; i < NO_OF_COMMANDS; i++) {
 
                 // match CMD or CMD? syntax if command is queryable
-                if (lookfor == cmd->text() && (!query || (query && cmd->queryable()))) {
+                if (lookfor == _commands[i].text() && (!query || (query && _commands[i].queryable()))) {
                     found = true;
                     if (args[0] == "HELP") {
-                        writef("%s%s", cmd->help(), newline);
+                        writef("%s%s", _commands[i].help().c_str(), newline);
                         write(done);
                     }
 
                     else if (args.size() > 1 && args[1] == "?") {
-                        writef("%s%s", cmd->usage().c_str(), newline);
+                        writef("%s%s", _commands[i].usage().c_str(), newline);
                         write(done);
-                    } else if (!cmd->verify(args)) {
-                        writef("%s%s", cmd->errorMessage().c_str(), newline);
+                    } else if (!_verify[i](args)) {
+                        writef("%s%s", _errorMessage.c_str(), newline);
                         writef("%s", error);
                     } else {
-                        if (cmd->action(args) == 0) {
+                        if (_action[i](args) == 0) {
                             writef("%s", done);
                         } else {
-                            writef("%s%s", cmd->errorMessage().c_str(), newline);
+                            writef("%s%s", _errorMessage.c_str(), newline);
                             writef("%s", error);
                         }
                     }
@@ -695,6 +994,7 @@
             }
         }
 
+#if defined(TARGET_MTS_MDOT_F411RE)
         if (history.size() == 0 || history.front() != command)
             history.push_front(command);
         history_index = -1;
@@ -702,23 +1002,271 @@
 
         while (history.size() > 10)
             history.pop_back();
+#else
+        command.clear();
+#endif
     }
 }
 
-std::string CommandTerminal::formatPacketData(const std::vector<uint8_t>& data, const uint8_t& format) {
-    if (format == mDot::HEXADECIMAL)
-        return mts::Text::bin2hexString(data);
-    else
-        return std::string(data.begin(), data.end());
-}
-
 void CommandTerminal::sleep(bool standby) {
     _xbee_on_sleep = GPIO_PIN_RESET;
 
     _serial.rxClear();
     _serial.txClear();
 
+#if defined(TARGET_XDOT_L151CC)
+    xdot_save_gpio_state();
+
+    /* GPIO Ports Clock Enable */
+    __GPIOA_CLK_ENABLE();
+    __GPIOB_CLK_ENABLE();
+    __GPIOC_CLK_ENABLE();
+    __GPIOH_CLK_ENABLE();
+
+    GPIO_InitTypeDef GPIO_InitStruct;
+
+    // UART1_TX, UART1_RTS & UART1_CTS to analog nopull - RX could be a wakeup source
+    GPIO_InitStruct.Pin = GPIO_PIN_9 | GPIO_PIN_11 | GPIO_PIN_12;
+    GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
+    GPIO_InitStruct.Pull = GPIO_NOPULL;
+    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
+
+    // I2C_SDA & I2C_SCL to analog nopull
+    GPIO_InitStruct.Pin = GPIO_PIN_8 | GPIO_PIN_9;
+    GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
+    GPIO_InitStruct.Pull = GPIO_NOPULL;
+    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
+
+    // SPI_MOSI, SPI_MISO, SPI_SCK, & SPI_NSS to analog nopull
+    GPIO_InitStruct.Pin = GPIO_PIN_12 | GPIO_PIN_13 | GPIO_PIN_14 | GPIO_PIN_15;
+    GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
+    GPIO_InitStruct.Pull = GPIO_NOPULL;
+    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
+
+    // iterate through potential wake pins - leave the configured wake pin alone if one is needed
+    if (_dot->getWakePin() != WAKE || _dot->getWakeMode() == mDot::RTC_ALARM) {
+        GPIO_InitStruct.Pin = GPIO_PIN_0;
+        GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
+        GPIO_InitStruct.Pull = GPIO_NOPULL;
+        HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
+    }
+    if (_dot->getWakePin() != GPIO0 || _dot->getWakeMode() == mDot::RTC_ALARM) {
+        GPIO_InitStruct.Pin = GPIO_PIN_4;
+        GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
+        GPIO_InitStruct.Pull = GPIO_NOPULL;
+        HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
+    }
+    if (_dot->getWakePin() != GPIO1 || _dot->getWakeMode() == mDot::RTC_ALARM) {
+        GPIO_InitStruct.Pin = GPIO_PIN_5;
+        GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
+        GPIO_InitStruct.Pull = GPIO_NOPULL;
+        HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
+    }
+    if (_dot->getWakePin() != GPIO2 || _dot->getWakeMode() == mDot::RTC_ALARM) {
+        GPIO_InitStruct.Pin = GPIO_PIN_0;
+        GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
+        GPIO_InitStruct.Pull = GPIO_NOPULL;
+        HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
+    }
+    if (_dot->getWakePin() != GPIO3 || _dot->getWakeMode() == mDot::RTC_ALARM) {
+        GPIO_InitStruct.Pin = GPIO_PIN_2;
+        GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
+        GPIO_InitStruct.Pull = GPIO_NOPULL;
+        HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
+    }
+    if (_dot->getWakePin() != UART1_RX || _dot->getWakeMode() == mDot::RTC_ALARM) {
+        GPIO_InitStruct.Pin = GPIO_PIN_10;
+        GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
+        GPIO_InitStruct.Pull = GPIO_NOPULL;
+        HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
+    }
+#else
+    uint32_t portA[6];
+    uint32_t portB[6];
+    uint32_t portC[6];
+    uint32_t portD[6];
+    uint32_t portH[6];
+
+    //Save the GPIO state.
+    portA[0] = GPIOA->MODER;
+    portA[1] = GPIOA->OTYPER;
+    portA[2] = GPIOA->OSPEEDR;
+    portA[3] = GPIOA->PUPDR;
+    portA[4] = GPIOA->AFR[0];
+    portA[5] = GPIOA->AFR[1];
+
+    portB[0] = GPIOB->MODER;
+    portB[1] = GPIOB->OTYPER;
+    portB[2] = GPIOB->OSPEEDR;
+    portB[3] = GPIOB->PUPDR;
+    portB[4] = GPIOB->AFR[0];
+    portB[5] = GPIOB->AFR[1];
+
+    portC[0] = GPIOC->MODER;
+    portC[1] = GPIOC->OTYPER;
+    portC[2] = GPIOC->OSPEEDR;
+    portC[3] = GPIOC->PUPDR;
+    portC[4] = GPIOC->AFR[0];
+    portC[5] = GPIOC->AFR[1];
+
+    portD[0] = GPIOD->MODER;
+    portD[1] = GPIOD->OTYPER;
+    portD[2] = GPIOD->OSPEEDR;
+    portD[3] = GPIOD->PUPDR;
+    portD[4] = GPIOD->AFR[0];
+    portD[5] = GPIOD->AFR[1];
+
+    portH[0] = GPIOH->MODER;
+    portH[1] = GPIOH->OTYPER;
+    portH[2] = GPIOH->OSPEEDR;
+    portH[3] = GPIOH->PUPDR;
+    portH[4] = GPIOH->AFR[0];
+    portH[5] = GPIOH->AFR[1];
+
+    /* GPIO Ports Clock Enable */
+    __GPIOA_CLK_ENABLE();
+    __GPIOB_CLK_ENABLE();
+    __GPIOC_CLK_ENABLE();
+
+    GPIO_InitTypeDef GPIO_InitStruct;
+
+    // Set port A pins to analog nopull
+    GPIO_InitStruct.Pin = GPIO_PIN_2 | GPIO_PIN_6 | GPIO_PIN_8 | GPIO_PIN_9 | GPIO_PIN_10 
+                | GPIO_PIN_12 | GPIO_PIN_13 | GPIO_PIN_14 | GPIO_PIN_15;
+    GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
+    GPIO_InitStruct.Pull = GPIO_NOPULL;
+    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);    
+
+    // Set port B pins to analog nopull
+    GPIO_InitStruct.Pin = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_3 | GPIO_PIN_4;
+    GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
+    GPIO_InitStruct.Pull = GPIO_NOPULL;
+    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); 
+
+    // Set port C pins to analog nopull
+    GPIO_InitStruct.Pin = GPIO_PIN_9 | GPIO_PIN_13;
+    GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
+    GPIO_InitStruct.Pull = GPIO_NOPULL;
+    HAL_GPIO_Init(GPIOC, &GPIO_InitStruct); 
+
+    // iterate through potential wake pins - leave the configured wake pin alone if one is needed
+    // XBEE_DIN - PA3
+    // XBEE_DIO2 - PA5
+    // XBEE_DIO3 - PA4
+    // XBEE_DIO4 - PA7
+    // XBEE_DIO5 - PC1
+    // XBEE_DIO6 - PA1
+    // XBEE_DIO7 - PA0
+    // XBEE_SLEEPRQ - PA11
+                
+    if (_dot->getWakePin() != XBEE_DIN || _dot->getWakeMode() == mDot::RTC_ALARM) {
+        GPIO_InitStruct.Pin = GPIO_PIN_3;
+        GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
+        GPIO_InitStruct.Pull = GPIO_NOPULL;
+        HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
+    }
+
+    if (_dot->getWakePin() != XBEE_DIO2 || _dot->getWakeMode() == mDot::RTC_ALARM) {
+        GPIO_InitStruct.Pin = GPIO_PIN_5;
+        GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
+        GPIO_InitStruct.Pull = GPIO_NOPULL;
+        HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
+    }
+
+    if (_dot->getWakePin() != XBEE_DIO3 || _dot->getWakeMode() == mDot::RTC_ALARM) {
+        GPIO_InitStruct.Pin = GPIO_PIN_4;
+        GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
+        GPIO_InitStruct.Pull = GPIO_NOPULL;
+        HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
+    }
+
+         if (_dot->getWakePin() != XBEE_DIO4 || _dot->getWakeMode() == mDot::RTC_ALARM) {
+        GPIO_InitStruct.Pin = GPIO_PIN_7;
+        GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
+        GPIO_InitStruct.Pull = GPIO_NOPULL;
+        HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
+    }
+
+     if (_dot->getWakePin() != XBEE_DIO5 || _dot->getWakeMode() == mDot::RTC_ALARM) {
+        GPIO_InitStruct.Pin = GPIO_PIN_1;
+        GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
+        GPIO_InitStruct.Pull = GPIO_NOPULL;
+        HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
+    }
+
+     if (_dot->getWakePin() != XBEE_DIO6 || _dot->getWakeMode() == mDot::RTC_ALARM) {
+        GPIO_InitStruct.Pin = GPIO_PIN_1;
+        GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
+        GPIO_InitStruct.Pull = GPIO_NOPULL;
+        HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
+    }
+
+     if (_dot->getWakePin() != XBEE_DIO7 || _dot->getWakeMode() == mDot::RTC_ALARM) {
+        GPIO_InitStruct.Pin = GPIO_PIN_0;
+        GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
+        GPIO_InitStruct.Pull = GPIO_NOPULL;
+        HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
+    }
+
+     if (_dot->getWakePin() != XBEE_SLEEPRQ|| _dot->getWakeMode() == mDot::RTC_ALARM) {
+        GPIO_InitStruct.Pin = GPIO_PIN_11;
+        GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
+        GPIO_InitStruct.Pull = GPIO_NOPULL;
+        HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
+    }
+ 
+#endif
     _dot->sleep(_dot->getWakeInterval(), _dot->getWakeMode(), standby);
+
+#if defined(TARGET_XDOT_L151CC)
+    xdot_restore_gpio_state();
+#else
+    //Restore the GPIO state.
+    GPIOA->MODER = portA[0];
+    GPIOA->OTYPER = portA[1];
+    GPIOA->OSPEEDR = portA[2];
+    GPIOA->PUPDR = portA[3];
+    GPIOA->AFR[0] = portA[4];
+    GPIOA->AFR[1] = portA[5];
+
+    GPIOB->MODER = portB[0];
+    GPIOB->OTYPER = portB[1];
+    GPIOB->OSPEEDR = portB[2];
+    GPIOB->PUPDR = portB[3];
+    GPIOB->AFR[0] = portB[4];
+    GPIOB->AFR[1] = portB[5];
+
+    GPIOC->MODER = portC[0];
+    GPIOC->OTYPER = portC[1];
+    GPIOC->OSPEEDR = portC[2];
+    GPIOC->PUPDR = portC[3];
+    GPIOC->AFR[0] = portC[4];
+    GPIOC->AFR[1] = portC[5];
+
+    GPIOD->MODER = portD[0];
+    GPIOD->OTYPER = portD[1];
+    GPIOD->OSPEEDR = portD[2];
+    GPIOD->PUPDR = portD[3];
+    GPIOD->AFR[0] = portD[4];
+    GPIOD->AFR[1] = portD[5];
+
+    GPIOH->MODER = portH[0];
+    GPIOH->OTYPER = portH[1];
+    GPIOH->OSPEEDR = portH[2];
+    GPIOH->PUPDR = portH[3];
+    GPIOH->AFR[0] = portH[4];
+    GPIOH->AFR[1] = portH[5];
+#endif
+
+    _serial.rxClear();
+    _serial.txClear();
+}
+
+std::string CommandTerminal::formatPacketData(const std::vector<uint8_t>& data, const uint8_t& format) {
+    if (format == mDot::HEXADECIMAL)
+        return mts::Text::bin2hexString(data);
+    else
+        return std::string(data.begin(), data.end());
 }
 
 bool CommandTerminal::waitForEscape(int timeout, mDot* dot, WaitType wait) {
@@ -745,59 +1293,18 @@
 }
 
 void CommandTerminal::wakeup(void) {
-    if (_dot->getWakePin() == XBEE_DIN) {
-        _serial.reattach(XBEE_DOUT, XBEE_DIN);
-        logInfo("Wakeup pin was serial input");
-    }
 }
 
-void CommandTerminal::RadioEvent::MacEvent(LoRaMacEventFlags* flags, LoRaMacEventInfo* info) {
+void CommandTerminal::RadioEvent::PacketRx(uint8_t port, uint8_t *payload, uint16_t size, int16_t rssi, int8_t snr, lora::DownlinkControl ctrl, uint8_t slot, uint8_t retries) {
+    mDotEvent::PacketRx(port, payload, size, rssi, snr, ctrl, retries);
 
-    if (mts::MTSLog::getLogLevel() == mts::MTSLog::TRACE_LEVEL) {
-        std::string msg = "OK";
-        switch (info->Status) {
-            case LORAMAC_EVENT_INFO_STATUS_ERROR:
-                msg = "ERROR";
-                break;
-            case LORAMAC_EVENT_INFO_STATUS_TX_TIMEOUT:
-                msg = "TX_TIMEOUT";
-                break;
-            case LORAMAC_EVENT_INFO_STATUS_RX_TIMEOUT:
-                msg = "RX_TIMEOUT";
-                break;
-            case LORAMAC_EVENT_INFO_STATUS_RX_ERROR:
-                msg = "RX_ERROR";
-                break;
-            case LORAMAC_EVENT_INFO_STATUS_JOIN_FAIL:
-                msg = "JOIN_FAIL";
-                break;
-            case LORAMAC_EVENT_INFO_STATUS_DOWNLINK_FAIL:
-                msg = "DOWNLINK_FAIL";
-                break;
-            case LORAMAC_EVENT_INFO_STATUS_ADDRESS_FAIL:
-                msg = "ADDRESS_FAIL";
-                break;
-            case LORAMAC_EVENT_INFO_STATUS_MIC_FAIL:
-                msg = "MIC_FAIL";
-                break;
-            default:
-                break;
+    if (serial_data_mode) {
+        logDebug("Rx %d bytes", size);
+        if (size > 0) {
+            CommandTerminal::Serial()->write((char*) RxPayload, RxPayloadSize);
         }
-        logTrace("Event: %s", msg.c_str());
-
-        logTrace("Flags Tx: %d Rx: %d RxData: %d RxSlot: %d LinkCheck: %d JoinAccept: %d",
-                 flags->Bits.Tx, flags->Bits.Rx, flags->Bits.RxData, flags->Bits.RxSlot, flags->Bits.LinkCheck, flags->Bits.JoinAccept);
-        logTrace("Info: Status: %d ACK: %d Retries: %d TxDR: %d RxPort: %d RxSize: %d RSSI: %d SNR: %d Energy: %d Margin: %d Gateways: %d",
-                 info->Status, info->TxAckReceived, info->TxNbRetries, info->TxDatarate, info->RxPort, info->RxBufferSize,
-                 info->RxRssi, info->RxSnr, info->Energy, info->DemodMargin, info->NbGateways);
-    }
-
-    if (flags->Bits.Rx) {
-        if (serial_data_mode) {
-            logDebug("Rx %d bytes", info->RxBufferSize);
-            if (info->RxBufferSize > 0) {
-                _serial.write((char*) info->RxBuffer, info->RxBufferSize);
-            }
+        if (!CommandTerminal::Serial()->readable() && _dot->getAckRequested() && _dot->getClass() == "C") {
+            _sendAck = true;
         }
     }
 }