dev

Dependencies:   MTS-Serial libmDot-mbed5

Fork of Dot-AT-Firmware by MultiTech

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;
         }
     }
 }