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:
9:ff62b20f7000
Parent:
6:e27eaad36a0c
Child:
11:05e0f30c03a6
diff -r a8be708e0e56 -r ff62b20f7000 CommandTerminal/CommandTerminal.cpp
--- a/CommandTerminal/CommandTerminal.cpp	Mon Apr 04 13:17:44 2016 +0000
+++ b/CommandTerminal/CommandTerminal.cpp	Mon Apr 04 09:00:31 2016 -0500
@@ -3,7 +3,6 @@
 #include "Command.h"
 #include "MTSLog.h"
 #include <cstdarg>
-
 #include <deque>
 
 const char CommandTerminal::banner[] = "\r\n\nMultiTech Systems LoRa XBee Module\r\n\n";
@@ -17,35 +16,43 @@
 // Response texts...
 const char CommandTerminal::help[] = "\r\nHelp\r\n";
 const char CommandTerminal::cmd_error[] = "Invalid command\r\n";
+const char CommandTerminal::connect[] = "\r\nCONNECT\r\n";
+const char CommandTerminal::no_carrier[] = "\r\nNO CARRIER\r\n";
 const char CommandTerminal::done[] = "\r\nOK\r\n";
 const char CommandTerminal::error[] = "\r\nERROR\r\n";
 
 // Escape sequence...
 const char CommandTerminal::escape_sequence[] = "+++";
 
-mts::MTSSerial* CommandTerminal::_serialp = NULL;
+mts::ATSerial* CommandTerminal::_serialp = NULL;
+
+static bool serial_data_mode = false;
+static bool peer_to_peer = false;
 
 void CommandTerminal::addCommand(Command* cmd) {
     _commands.push_back(cmd);
 }
 
-CommandTerminal::CommandTerminal(mts::MTSSerial& serial, mDot* dot)
+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),
-  _serial_up(false) {
+  _xbee_on_sleep(XBEE_ON_SLEEP) {
 
+    _dot->setEvents(_events);
+    _dot->setWakeupCallback(this, &CommandTerminal::wakeup);
     _serialp = &serial;
 
     addCommand(new CmdAttention(_dot));
     addCommand(new CmdIdentification(_dot, serial));
     addCommand(new CmdResetCpu(_dot, serial));
-    addCommand(new CmdDummy(_dot, "Enable/Disable Echo", "ATE0/1", "ATE0: disable, ATE1: enable"));
-    addCommand(new CmdDummy(_dot, "Enable/Disable Verbose", "ATV0/1", "ATV0: disable, ATV1: enable"));
+    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"));
 
     addCommand(new CmdFactoryDefault(_dot));
     addCommand(new CmdSaveConfig(_dot));
@@ -60,17 +67,25 @@
     addCommand(new CmdFrequencySubBand(_dot, serial));
     addCommand(new CmdPublicNetwork(_dot, serial));
     addCommand(new CmdDeviceId(_dot, serial));
+    addCommand(new CmdDeviceClass(_dot, serial));
 
+    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));
 
+    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));
@@ -81,8 +96,11 @@
     addCommand(new CmdSnr(_dot, serial));
     addCommand(new CmdDataPending(_dot, serial));
 
+    addCommand(new CmdSessionDataRate(_dot, serial));
+
     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));
@@ -90,8 +108,7 @@
     addCommand(new CmdTxNextMs(_dot, serial));
     addCommand(new CmdTimeOnAir(_dot, serial));
 
-    addCommand(new CmdRxDataRate(_dot, serial));
-    addCommand(new CmdRxFrequency(_dot, serial));
+    addCommand(new CmdRxDelay(_dot, serial));
     addCommand(new CmdRxOutput(_dot, serial));
     addCommand(new CmdRxInverted(_dot, serial));
 
@@ -100,23 +117,28 @@
     addCommand(new CmdAdaptiveDataRate(_dot, serial));
 
     addCommand(new CmdACKAttempts(_dot, serial));
+    addCommand(new CmdRepeat(_dot, serial));
 
     addCommand(new CmdSendString(_dot, serial));
-    addCommand(new CmdSendStringHighBW(_dot, serial));
     addCommand(new CmdSendBinary(_dot, serial));
-    addCommand(new CmdSendStringOnInterval(_dot, serial));
     addCommand(new CmdReceiveOnce(_dot, serial));
-    addCommand(new CmdReceiveContinuous(_dot, serial));
 
-    addCommand(new CmdDummy(_dot, "Serial Data Mode", "AT+SD", "Reads serial data, sends packet, then sleeps using wake settings"));
-    addCommand(new CmdDummy(_dot, "Sleep Mode", "AT+SLEEP", "Enter sleep mode"));
+    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 CmdWakePin(_dot, serial));
     addCommand(new CmdWakeDelay(_dot, serial));
     addCommand(new CmdWakeTimeout(_dot, serial));
     addCommand(new CmdPing(_dot, serial));
     addCommand(new CmdLogLevel(_dot, serial));
+
+    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));
 }
 
 void CommandTerminal::printHelp() {
@@ -191,129 +213,133 @@
     va_end(ap);
 }
 
-void CommandTerminal::serial_loop() {
+void CommandTerminal::serialLoop() {
     Timer serial_read_timer;
     std::vector<uint8_t> serial_buffer;
-    std::string escape_buffer;
-    Timer escape_timer;
-    int escape_delay = 100;
-    uint8_t max_send_size;
-
-    _serial_up = true;
-    _xbee_on_sleep = GPIO_PIN_SET;
+    std::vector<uint8_t> data;
+    int timeout = 0;
 
-    if (_dot->getFrequencyBand() == mDot::FB_915)
-        max_send_size = mDot::MaxLengths_915[_dot->getTxDataRate()];
-    else
-        max_send_size = mDot::MaxLengths_868[_dot->getTxDataRate()];
-
-    logDebug("Awake\r\n");
-    wakeup(_sleep_standby);
-
-    char ch;
+    serial_read_timer.start();
 
-    if (readable()) {
-        ch = read();
-        serial_buffer.push_back(ch);
+    if (_dot->getStartUpMode() == mDot::SERIAL_MODE) {
+        _xbee_on_sleep = GPIO_PIN_SET;
 
-        if (escape_timer.read_ms() > escape_delay && ch == '+') {
-            escape_buffer += ch;
-            escape_timer.reset();
-        } else {
-            _serial_up = true;
-            escape_buffer.clear();
-        }
+        timeout = _dot->getWakeDelay();
 
-        if (escape_buffer.length() == 3 && escape_buffer.find(escape_sequence) == 0) {
-            _mode = mDot::COMMAND_MODE;
-            logDebug("Exit serial mode\r\n");
-            escape_timer.stop();
-            escape_buffer.clear();
-            write(done);
-            return;
+        // wait for timeout or start of serial data
+        while (!readable() && serial_read_timer.read_ms() < timeout && !_serial.escaped()) {
+            osDelay(2);
         }
     }
 
-    if (_serial_up) {
-        serial_read_timer.start();
-        uint32_t timeout = _dot->getWakeDelay();
-
-        // wait for timeout or start of serial data
-        while (!readable() && serial_read_timer.read_ms() < timeout) {
-            osDelay(10);
-        }
+    if (readable() && !_serial.escaped()) {
 
         serial_read_timer.reset();
         timeout = _dot->getWakeTimeout();
-        while (_serial_up && serial_read_timer.read_ms() < timeout) {
-            while (readable() && serial_buffer.size() < max_send_size) {
+
+        while (serial_read_timer.read_ms() < timeout && serial_buffer.size() <= _dot->getMaxPacketLength()) {
+            while (readable() && serial_buffer.size() < _dot->getMaxPacketLength()) {
                 serial_buffer.push_back(read());
                 serial_read_timer.reset();
+
+                if (_serial.escaped())
+                    break;
             }
         }
+
         serial_read_timer.stop(), serial_read_timer.reset();
 
         if (!serial_buffer.empty()) {
-            _serial_up = false;
-            _xbee_on_sleep = GPIO_PIN_RESET;
+            if (_dot->getStartUpMode() == mDot::SERIAL_MODE)
+                _xbee_on_sleep = GPIO_PIN_RESET;
+
+            // wait for any duty cycle limit to expire
+            while (_dot->getNextTxMs() > 0 && !_serial.escaped()) {
+                osDelay(10);
+            }
+
             if (!_dot->getIsTransmitting()) {
-                std::vector<uint8_t> recv_buffer;
-                logDebug("Received serial data, sending out radio.\r\n");
+                logDebug("Received serial data, sending out radio.");
+
+                if (_dot->send(serial_buffer, false) != mDot::MDOT_OK) {
+                    logDebug("Send failed.");
+                    // If the data should be tossed after send failure, clear buffer
+                    if (_dot->getSerialClearOnError()) {
+                        serial_buffer.clear();
+                    }
+                } else {
 
-                if (_dot->send(serial_buffer) != mDot::MDOT_OK)
-                    logDebug("Send failed.\r\n");
-                if (_dot->recv(recv_buffer))
-                    _serial.writef("%s\r\n", formatPacketData(recv_buffer, _dot->getRxOutput()).c_str());
+                    // wait for send to finish
+                    while (_dot->getIsTransmitting() && !_serial.escaped())
+                        osDelay(10);
+
+                    // call recv to wait for any packet before sending again
+                    if (!_serial.escaped())
+                        _dot->recv(data);
+
+                    // Clear the serial buffer if send is success
+                    serial_buffer.clear();
+                }
             } else {
                 logDebug("Radio is busy, cannot send.\r\n");
+                osDelay(10);
             }
 
-            serial_buffer.clear();
         } else {
             logDebug("No data received from serial to send.\r\n");
         }
-        _serial_up = false;
+    }
+
+    if (!_serial.readable() && _dot->getStartUpMode() == mDot::SERIAL_MODE && !_serial.escaped()) {
+        _xbee_on_sleep = GPIO_PIN_RESET;
+        sleep(_sleep_standby);
     }
-    sleep(_sleep_standby);
+
+    if (_serial.escaped()) {
+        _serial.clearEscaped();
+        _serial.rxClear();
+        serial_data_mode = false;
+        _mode = mDot::COMMAND_MODE;
+        logDebug("Exit Serial Mode");
+        write(done);
+        return;
+    }
+
+    if (!_dot->getNetworkJoinStatus()) {
+        serial_data_mode = false;
+        _mode = mDot::COMMAND_MODE;
+        logDebug("Exit Serial Mode");
+        write(no_carrier);
+        return;
+    }
 }
 
 bool CommandTerminal::autoJoinCheck() {
 
     std::string escape_buffer;
-    char ch;
     int sleep = 1000;
-    int escape_timeout = 1000;
     Timer tmr;
-    Timer escape_tmr;
+    tmr.start();
     int cnt = 0;
 
     while (!_dot->getNetworkJoinStatus()) {
-        wakeup(_sleep_standby);
         write("\r\nJoining network... ");
 
-        // wait one second for possible escape
-        tmr.reset();
-        tmr.start();
-        escape_tmr.reset();
-        escape_tmr.start();
-        while (tmr.read_ms() < 1000) {
-            if (_serial.readable()) {
-                _serial.read(&ch, 1);
-                escape_buffer += ch;
+        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);
+
+            tmr.reset();
+            while (_dot->getNextTxMs() > 0 && !_serial.escaped()) {
+                osDelay(2);
             }
 
-            if (escape_buffer.find(escape_sequence) != std::string::npos) {
-                _mode = mDot::COMMAND_MODE;
-                write("Join Canceled\r\n");
-                write(done);
-                return true;
-            }
-
-            if (escape_tmr.read_ms() > escape_timeout)
-                escape_buffer.clear();
+            tmr.reset();
+            while (tmr.read_ms() < rand_time && !_serial.escaped())
+                osDelay(10);
         }
 
-        if (_dot->joinNetworkOnce() == mDot::MDOT_OK) {
+        if (!_serial.escaped() && _dot->joinNetworkOnce() == mDot::MDOT_OK) {
             write("Network Joined\r\n");
             write(done);
             return false;
@@ -322,12 +348,12 @@
         write("Network Join failed\r\n");
         write(error);
 
-        if (cnt++ > _dot->getJoinRetries()) {
+        if (!_serial.escaped() && _dot->getJoinRetries() > 0 && cnt++ > _dot->getJoinRetries()) {
             cnt = 0;
 
             if (_dot->getFrequencyBand() == mDot::FB_915) {
                 uint8_t band = ((_dot->getFrequencySubBand()) % 8) + 1;
-                logDebug("Join retries exhausted, switching to sub band %u\r\n", band);
+                logWarning("Join retries exhausted, switching to sub band %u", band);
                 _dot->setFrequencySubBand(band);
             }
 
@@ -336,24 +362,18 @@
         }
 
         tmr.reset();
-        tmr.start();
-        escape_tmr.reset();
-        escape_tmr.start();
-        while (tmr.read_ms() < sleep) {
-            if (_serial.readable()) {
-                _serial.read(&ch, 1);
-                escape_buffer += ch;
-            }
-
-            if (escape_buffer.find(escape_sequence) != std::string::npos) {
-                _mode = mDot::COMMAND_MODE;
-                return true;
-            }
-
-            if (escape_tmr.read_ms() > escape_timeout)
-                escape_buffer.clear();
+        while (tmr.read_ms() < sleep && !_serial.escaped()) {
+            osDelay(10);
         }
 
+        if (_serial.escaped()) {
+            _serial.clearEscaped();
+            serial_data_mode = false;
+            _mode = mDot::COMMAND_MODE;
+            write("Join Canceled\r\n");
+            write(done);
+            return true;
+        }
     }
 
     return false;
@@ -361,8 +381,6 @@
 
 void CommandTerminal::start() {
 
-    wakeup(_sleep_standby);
-
     char ch;
     bool running = true;
     bool echo = _dot->getEcho();
@@ -370,65 +388,98 @@
     std::deque<std::string> history;
     int history_index = -1;
     std::vector<std::string> args;
+    bool join_canceled = false;
 
     if (_dot->getStartUpMode() == mDot::SERIAL_MODE) {
-        command = "AT+SD\n";
+
+        serial_data_mode = true;
+        _mode = mDot::SERIAL_MODE;
 
         std::string escape_buffer;
         char ch;
 
-        int escape_timeout = 1000;
-        Timer tmr;
-        Timer escape_tmr;
+        if (!_dot->getStandbyFlag()) {
+            // wake up from power-on/reset
+
+            int escape_timeout = 1000;
+            Timer tmr;
+            Timer escape_tmr;
 
-        // wait one second for possible escape
-        tmr.reset();
-        tmr.start();
-        escape_tmr.reset();
-        escape_tmr.start();
-        while (tmr.read_ms() < escape_timeout) {
-            if (_serial.readable()) {
-                _serial.read(&ch, 1);
-                escape_buffer += ch;
-            }
+            // wait one second for possible escape by user pressing '+' key
+            tmr.reset();
+            tmr.start();
+            escape_tmr.reset();
+            escape_tmr.start();
+            while (tmr.read_ms() < escape_timeout) {
+                if (_serial.readable()) {
+                    _serial.read(&ch, 1);
+                    escape_buffer += ch;
+                }
 
-            if (escape_buffer.find(escape_sequence) != std::string::npos) {
-                _mode = mDot::COMMAND_MODE;
-                command.clear();
-                break;
+                if (escape_buffer.find("+") != std::string::npos) {
+                    logInfo("Escape detected");
+                    join_canceled = true;
+                    serial_data_mode = false;
+                    _mode = mDot::COMMAND_MODE;
+                    command.clear();
+                    break;
+                }
+
+                if (escape_tmr.read_ms() > escape_timeout)
+                    escape_buffer.clear();
+
+                osDelay(1);
             }
-
-            if (escape_tmr.read_ms() > escape_timeout)
-                escape_buffer.clear();
-
-            osDelay(1);
         }
 
+        if (_mode == mDot::SERIAL_MODE && !_dot->getNetworkJoinStatus() && _dot->getJoinMode() == mDot::OTA) {
+            if (_dot->joinNetworkOnce() != mDot::MDOT_OK) {
+                serial_data_mode = false;
+                _mode = mDot::COMMAND_MODE;
+
+                logWarning("Start Up Mode set to SERIAL_MODE, but join failed.");
+                _serial.writef("Network Not Joined\r\n");
+                _serial.writef(error);
+            }
+        }
     }
 
-    bool join_canceled = false;
+    if (_dot->getJoinMode() == mDot::PEER_TO_PEER) {
+        peer_to_peer = true;
+    } else {
+        peer_to_peer = false;
+    }
+
+
 
     //Run terminal session
     while (running) {
-        if (!join_canceled && _dot->getJoinMode() == mDot::AUTO_OTA) {
-            join_canceled = autoJoinCheck();
-            if (join_canceled)
-                command.clear();
-        }
+
+        // wait for input to reduce at command idle current
+        while (!readable() || _mode == mDot::SERIAL_MODE) {
+            if (!join_canceled && _dot->getJoinMode() == mDot::AUTO_OTA) {
+                join_canceled = autoJoinCheck();
+                if (join_canceled)
+                    command.clear();
+            }
 
-        if (_dot->getJoinMode() != mDot::AUTO_OTA || (!join_canceled && _dot->getJoinMode() == mDot::AUTO_OTA)) {
-            switch (_mode) {
-                case mDot::SERIAL_MODE:
-                    // signal wakeup, read serial and output to radio
-                    serial_loop();
-                    continue;
-                    break;
-                default:
-                    break;
+            if (_dot->getJoinMode() != mDot::AUTO_OTA || (!join_canceled && _dot->getJoinMode() == mDot::AUTO_OTA)) {
+                switch (_mode) {
+                    case mDot::SERIAL_MODE:
+                        // signal wakeup, read serial and output to radio
+                        serialLoop();
+                        continue;
+                        break;
+                    default:
+                        break;
+                }
             }
-        }
+
+            ch = '\0';
 
-        ch = '\0';
+            wait(0.00001); // 10 us
+            _serial.escaped();
+        }
 
         // read characters
         if (readable()) {
@@ -443,7 +494,7 @@
             } else if (ch == 0x1b || ch == 0x09) {
                 osDelay(20);
                 // catch escape sequence, or tab
-                char ch1, ch2;
+                char ch1 = 0x00, ch2 = 0x00;
 
                 if (readable()) {
                     ch1 = read();
@@ -452,11 +503,11 @@
 
                     if (ch1 == 0x5b && ch2 == 0x41) {
                         // up key
-                        for (int i = 0; i < command.size()+1; i++) {
+                        for (size_t i = 0; i < command.size() + 1; i++) {
                             writef("\b \b");
                         }
                         if (history.size() > 0) {
-                            if (++history_index >= history.size() - 1)
+                            if (++history_index >= int(history.size() - 1))
                                 history_index = history.size() - 1;
 
                             command = history[history_index];
@@ -467,7 +518,7 @@
                     } else if (ch1 == 0x5b && ch2 == 0x42) {
 
                         // down key
-                        for (int i = 0; i < command.size()+1; i++) {
+                        for (size_t i = 0; i < command.size() + 1; i++) {
                             writef("\b \b");
                         }
 
@@ -480,7 +531,8 @@
                         }
                     }
                 }
-                while (readable()) read();
+                while (readable())
+                    read();
                 continue;
             } else {
                 command += ch;
@@ -537,6 +589,9 @@
         if ((args[0].find("?") == 0 || args[0].find("HELP") == 0) && args.size() == 1) {
             printHelp();
             command.clear();
+        } else if ((args[0].find("ATE?") == 0 && args[0].length() == 4) || (args[0].find("ATE") == 0 && args[0].length() == 3)) {
+            writef("%d\r\n", _dot->getEcho());
+            write(done);
         } else if (args[0].find("ATE0") == 0 && args[0].length() == 4) {
             _dot->setEcho(false);
             write(done);
@@ -545,28 +600,48 @@
             _dot->setEcho(true);
             write(done);
             echo = _dot->getEcho();
+        } else if ((args[0].find("ATV?") == 0 && args[0].length() == 4) || (args[0].find("ATV") == 0 && args[0].length() == 3)) {
+            writef("%d\r\n", _dot->getVerbose());
+            write(done);
         } else if (args[0].find("ATV0") == 0 && args[0].length() == 4) {
             _dot->setVerbose(false);
             write(done);
         } else if (args[0].find("ATV1") == 0 && args[0].length() == 4) {
             _dot->setVerbose(true);
             write(done);
+        } else if ((args[0].find("AT&K?") == 0 && args[0].length() == 5) || (args[0].find("AT&K") == 0 && args[0].length() == 4)) {
+            writef("%d\r\n", (_dot->getFlowControl() ? 3 : 0));
+            write(done);
+        } else if (args[0].find("AT&K0") == 0 && args[0].length() == 5) {
+            _dot->setFlowControl(false);
+            write(done);
+        } else if (args[0].find("AT&K3") == 0 && args[0].length() == 5) {
+            _dot->setFlowControl(true);
+            write(done);
         } else if (args[0] == "AT+SD") {
-            logDebug("Enter Serial Mode\r\n");
-            _mode = mDot::SERIAL_MODE;
+            if (_dot->getNetworkJoinStatus()) {
+                logDebug("Enter Serial Mode");
+                write(connect);
+                serial_data_mode = true;
+                _mode = mDot::SERIAL_MODE;
+            } else {
+                logDebug("Network Not Joined");
+                write("Network Not Joined\r\n");
+                write(error);
+            }
         } else if (args[0] == "AT+SLEEP") {
-            if (args.size() > 1 && (args[1] != "?")) {
+            if (args.size() > 2 && (args[1] != "?")) {
                 write("Invalid argument\r\n");
                 write(error);
             } else {
                 if (args.size() > 1 && args[1] == "?") {
-                    write("AT+SLEEP: NONE\r\n");
+                    write("(0:deepsleep,1:sleep)\r\n");
                     write(done);
                 } else {
                     _sleep_standby = !(args.size() > 1 && args[1] == "1");
+                    write(done);
                     this->sleep(_sleep_standby);
                     wait(0.1);
-                    write(done);
                 }
             }
         } else {
@@ -638,7 +713,6 @@
 }
 
 void CommandTerminal::sleep(bool standby) {
-    _serial_up = false;
     _xbee_on_sleep = GPIO_PIN_RESET;
 
     _serial.rxClear();
@@ -649,9 +723,6 @@
 
 bool CommandTerminal::waitForEscape(int timeout, mDot* dot, WaitType wait) {
     Timer timer;
-    Timer escape_timer;
-    std::string escape_buffer;
-    int escape_timeout = 1000;
 
     timer.start();
     while (timer.read_ms() < timeout) {
@@ -662,18 +733,9 @@
             }
         }
 
-        if (_serialp != NULL && _serialp->readable()) {
-            if (escape_buffer == "")
-                escape_timer.start();
-            char ch;
-            _serialp->read(&ch, 1);
-            escape_buffer += ch;
-            if (escape_buffer == CommandTerminal::escape_sequence)
-                return true;
-        }
-        if (escape_timer.read_ms() > escape_timeout) {
-            escape_buffer = "";
-            escape_timer.stop(), escape_timer.reset();
+        if (_serialp != NULL && _serialp->escaped()) {
+            _serialp->clearEscaped();
+            return true;
         }
 
         osDelay(10);
@@ -682,6 +744,66 @@
     return false;
 }
 
-void CommandTerminal::wakeup(bool standby) {
+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) {
 
+    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;
+        }
+        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);
+            }
+        }
+
+        delete[] info->RxBuffer;
+    }
 }
+
+CommandTerminal::~CommandTerminal() {
+    delete _events;
+}