AT command firmware for MultiTech Dot devices.

Fork of mDot_AT_firmware by MultiTech

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers CommandTerminal.cpp Source File

CommandTerminal.cpp

00001 #include "ctype.h"
00002 #include "CommandFactory.h"
00003 #include "CommandTerminal.h"
00004 #include "Command.h"
00005 #include "MTSLog.h"
00006 #include "ChannelPlan.h"
00007 #include <cstdarg>
00008 #include <deque>
00009 #include "mts_at_version.h"
00010 #include "LowPower.h"
00011 
00012 #if defined(TARGET_XDOT_L151CC)
00013 #include "xdot_low_power.h"
00014 #endif
00015 
00016 #if defined(TARGET_MTS_MDOT_F411RE) || defined(TARGET_XDOT_MAX32670)
00017 #define MTS_DOT_COMMAND_HISTORY_FEATURE 1
00018 #endif
00019 
00020 #if !defined(TARGET_XDOT_L151CC) || defined(MTS_RADIO_DEBUG_COMMANDS) || defined(MTS_CERT_TEST_MODE)
00021 #define MTS_DOT_ENABLE_LORAWAN_TESTMODE 1
00022 #endif
00023 
00024 
00025 const char CommandTerminal::newline[] = "\r\n";
00026 
00027 // Command error text...
00028 const char CommandTerminal::command_error[] = "Command not found!\r\n";
00029 
00030 // Response texts...
00031 const char CommandTerminal::help[] = "\r\nHelp\r\n";
00032 const char CommandTerminal::cmd_error[] = "Invalid command\r\n";
00033 const char CommandTerminal::connect[] = "\r\nCONNECT\r\n";
00034 const char CommandTerminal::no_carrier[] = "\r\nNO CARRIER\r\n";
00035 const char CommandTerminal::done[] = "\r\nOK\r\n";
00036 const char CommandTerminal::error[] = "\r\nERROR\r\n";
00037 
00038 // Escape sequence...
00039 const char CommandTerminal::escape_sequence[] = "+++";
00040 
00041 mts::ATSerial* CommandTerminal::_serialp = NULL;
00042 mDot* CommandTerminal::_dot = NULL;
00043 
00044 CommandTerminal::RadioEvent* CommandTerminal::_events = new RadioEvent();
00045 
00046 static bool serial_data_mode = false;
00047 static bool peer_to_peer = false;
00048 static bool command_processing = false;
00049 static bool urc_enabled = false;
00050 
00051 std::string CommandTerminal::_errorMessage = "";
00052 
00053 static uint8_t _battery_level = 0xFF;
00054 
00055 static uint8_t f_data[252]; //max payload 242 plus 10 bytes for format
00056 static uint32_t _rxAddress = 0;
00057 static uint32_t _rxFcnt = 0;
00058 
00059 #if defined(TARGET_MTS_MDOT_F411RE)
00060 #define PIN_PACKET_RX       D12
00061 #define PIN_JOIN_STATUS     A2
00062 #define PIN_ON_SLEEP        XBEE_ON_SLEEP
00063 #elif defined(TARGET_XDOT_L151CC)
00064 #define PIN_PACKET_RX       GPIO1
00065 #define PIN_JOIN_STATUS     GPIO0
00066 #define PIN_ON_SLEEP        GPIO2
00067 #elif defined(TARGET_MAX32630FTHR)
00068 #define PIN_PACKET_RX       LED_RED
00069 #define PIN_JOIN_STATUS     LED3
00070 #define PIN_ON_SLEEP        P5_5
00071 #elif defined(TARGET_XDOT_MAX32670) || defined(TARGET_XDOTES_MAX32670)
00072 #define PIN_PACKET_RX       GPIO1
00073 #define PIN_JOIN_STATUS     GPIO0
00074 #define PIN_ON_SLEEP        GPIO2
00075 #elif defined(TARGET_MAX32660EVSYS) || defined(TARGET_MAX32660EVSYS)
00076 // NO-OP: no pins to define, will default to NC
00077 #else
00078 #error Unsupported target
00079 #endif
00080 
00081 #ifndef GPIO_PIN_SET
00082 #define GPIO_PIN_SET 1
00083 #endif
00084 
00085 #ifndef GPIO_PIN_RESET
00086 #define GPIO_PIN_RESET 0
00087 #endif
00088 
00089 
00090 #if defined(PIN_PACKET_RX)
00091 DigitalOut _packet_rx_pin(PIN_PACKET_RX);
00092 #define USE_PIN_PACKET_RX 1
00093 #else
00094 #define PIN_PACKET_RX      NC
00095 #endif
00096 
00097 #if defined(PIN_JOIN_STATUS)
00098 DigitalOut _join_status_pin(PIN_JOIN_STATUS);
00099 #define USE_PIN_JOIN_STATUS 1
00100 #else
00101 #define PIN_JOIN_STATUS     NC
00102 #endif
00103 
00104 #if defined(PIN_ON_SLEEP)
00105 DigitalOut _xbee_on_sleep(PIN_ON_SLEEP);
00106 #define USE_PIN_ON_SLEEP 1
00107 #else
00108 #define PIN_ON_SLEEP        NC
00109 #endif
00110 
00111 void CommandTerminal::setErrorMessage(const char* message) {
00112     _errorMessage.assign(message);
00113 }
00114 
00115 void CommandTerminal::setErrorMessage(const std::string& message) {
00116     _errorMessage.assign(message);
00117 }
00118 
00119 CommandTerminal::CommandTerminal(mts::ATSerial& serial) :
00120   _serial(serial),
00121   _mode(mDot::COMMAND_MODE),
00122   _sleep_standby(true),
00123   _autoOTAEnabled(false)
00124 {
00125     _serialp = &serial;
00126 }
00127 
00128 void free_mem() {
00129     // In order to get free mem within RTOS
00130     // we need to get the main thread's stack pointer
00131     // and subtract it with the top of the heap
00132     // ------+-------------------+   Last Address of RAM (INITIAL_SP)
00133     //       | Scheduler Stack   |
00134     //       +-------------------+
00135     //       | Main Thread Stack |
00136     //       |         |         |
00137     //       |         v         |
00138     //       +-------------------+ <- bottom_of_stack/__get_MSP()
00139     // RAM   |                   |
00140     //       |  Available RAM    |
00141     //       |                   |
00142     //       +-------------------+ <- top_of_heap
00143     //       |         ^         |
00144     //       |         |         |
00145     //       |       Heap        |
00146     //       +-------------------+ <- __end__ / HEAP_START (linker defined var)
00147     //       | ZI                |
00148     //       +-------------------+
00149     //       | ZI: Shell Stack   |
00150     //       +-------------------+
00151     //       | ZI: Idle Stack    |
00152     //       +-------------------+
00153     //       | ZI: Timer Stack   |
00154     //       +-------------------+
00155     //       | RW                |
00156     // ------+===================+  First Address of RAM
00157     //       |                   |
00158     // Flash |                   |
00159     //
00160 
00161     uint32_t bottom_of_stack = __get_MSP();
00162     char* top_of_heap =  (char *) malloc(sizeof(char));
00163     uint32_t diff = bottom_of_stack - (uint32_t) top_of_heap;
00164 
00165     free((void *) top_of_heap);
00166 
00167     CommandTerminal::Serial()->writef("%lu bytes\r\n", diff);
00168 }
00169 
00170 void CommandTerminal::init() {
00171     _dot->setEvents(_events);
00172     _serial.rxClear();
00173     _serial.txClear();
00174 }
00175 
00176 #if MTS_CMD_TERM_VERBOSE
00177 void CommandTerminal::printHelp() {
00178     const char* name = NULL;
00179     const char* text = NULL;
00180     const char* desc = NULL;
00181     const char* tab = "\t";
00182 
00183     std::string header("Command");
00184     header.append(tab);
00185     header.append(tab);
00186     header.append("Name");
00187     header.append(tab);
00188     header.append(tab);
00189     header.append(tab);
00190     header.append("Description");
00191 
00192     write(newline);
00193     write(header.c_str());
00194     write(newline);
00195     write(newline);
00196 
00197     Command *cmd = NULL;
00198     for (int i = 0; i < CommandFactory::NUMBER_OF_CMDS; i++) {
00199         cmd = CommandFactory::Create(static_cast<CommandFactory::CmdId_t>(i));
00200         name = cmd->name();
00201         text = cmd->text();
00202         desc = cmd->desc();
00203         write(text);
00204         if (strlen(text) < 8)
00205             write(tab);
00206         write(tab);
00207         write(name);
00208         if (strlen(name) < 8)
00209             write(tab);
00210         if (strlen(name) < 16)
00211             write(tab);
00212         write(tab);
00213         write(desc);
00214         write(newline);
00215 
00216         delete cmd;
00217     }
00218 
00219     write(newline);
00220 }
00221 #endif
00222 
00223 bool CommandTerminal::writeable() {
00224     return _serialp->writeable();
00225 }
00226 
00227 bool CommandTerminal::readable() {
00228     return _serialp->readable();
00229 }
00230 
00231 char CommandTerminal::read() {
00232     char ch;
00233     _serialp->read(ch);
00234     return ch;
00235 }
00236 
00237 void CommandTerminal::write(const char* message) {
00238     while (!writeable())
00239         ;
00240     _serialp->write(message, strlen(message));
00241 }
00242 
00243 void CommandTerminal::writef(const char* format, ...) {
00244     char buff[512];
00245 
00246     va_list ap;
00247     va_start(ap, format);
00248     int size = vsnprintf(buff, 256, format, ap);
00249     while (!writeable())
00250         ;
00251     _serialp->write(buff, size);
00252     va_end(ap);
00253 }
00254 
00255 void CommandTerminal::serialLoop() {
00256     Timer serial_read_timer;
00257     std::vector<uint8_t> serial_buffer;
00258     std::vector<uint8_t> data;
00259     std::chrono::milliseconds timeout(0);
00260     std::chrono::milliseconds elapsed_time_ms;
00261 
00262     serial_read_timer.start();
00263 
00264     if (_dot->getStartUpMode() == mDot::SERIAL_MODE) {
00265 #if defined(USE_PIN_ON_SLEEP)
00266         _xbee_on_sleep = GPIO_PIN_SET;
00267 #endif
00268         timeout = std::chrono::milliseconds(_dot->getWakeDelay());
00269         elapsed_time_ms = std::chrono::duration_cast<std::chrono::milliseconds>(serial_read_timer.elapsed_time());
00270 
00271         // wait for timeout or start of serial data
00272         while (!readable() && elapsed_time_ms < timeout && !_serialp->escaped()) {
00273             ThisThread::sleep_for(2ms);
00274             elapsed_time_ms = std::chrono::duration_cast<std::chrono::milliseconds>(serial_read_timer.elapsed_time());
00275         }
00276     }
00277 
00278     if (!readable() && _events->SendAck()) {
00279         logDebug("SERIAL NOT READABLE and ACK REQUESTED");
00280         goto L_SEND;
00281     }
00282 
00283     if (readable() && !_serialp->escaped()) {
00284 
00285         serial_read_timer.reset();
00286         timeout = std::chrono::milliseconds(_dot->getWakeTimeout());
00287 
00288         while (true) {
00289             elapsed_time_ms = std::chrono::duration_cast<std::chrono::milliseconds>(serial_read_timer.elapsed_time());
00290             if ((elapsed_time_ms >= timeout) ) {
00291 
00292                 break;
00293             }
00294             if (serial_buffer.size() >= _dot->getNextTxMaxSize())  {
00295                 break;
00296             }
00297             if  (_serialp->escaped())  {
00298                 break;
00299             }
00300             if (readable()) {
00301                 serial_buffer.push_back(read());
00302                 serial_read_timer.reset();
00303             } else {
00304                 ThisThread::sleep_for(5ms);
00305             }
00306         }
00307 
00308         serial_read_timer.stop(), serial_read_timer.reset();
00309 
00310         if (!serial_buffer.empty()) {
00311             if (_dot->getStartUpMode() == mDot::SERIAL_MODE)
00312 #if defined(USE_PIN_ON_SLEEP)
00313                  _xbee_on_sleep = GPIO_PIN_RESET;
00314 #endif
00315 
00316 L_SEND:
00317             // wait for any duty cycle limit to expire
00318             while (_dot->getNextTxMs() > 0 && !_serialp->escaped()) {
00319                 ThisThread::sleep_for(10ms);
00320             }
00321 
00322             if (_dot->getIsIdle()) {
00323                 logDebug("Received serial data, sending out radio.");
00324                 if(_dot->getRxOutput() == mDot::EXTENDED || _dot->getRxOutput() == mDot::EXTENDED_HEX) {
00325                     formatPacketSDSend(serial_buffer);
00326                 }
00327 
00328                 if (_dot->send(serial_buffer, false) != mDot::MDOT_OK) {
00329                     logDebug("Send failed.");
00330                     // If the data should be tossed after send failure, clear buffer
00331                     if (_dot->getSerialClearOnError()) {
00332                         serial_buffer.clear();
00333                     }
00334                 } else {
00335 
00336                     // wait for send to finish
00337                     do {
00338                         ThisThread::sleep_for(50ms);
00339                     } while (!_dot->getIsIdle() && !_serialp->escaped());
00340 
00341                     // call recv to wait for any packet before sending again
00342                     if (!_serialp->escaped()) {
00343                         data.clear();
00344                         _dot->recv(data);
00345                     }
00346 
00347                     // Clear the serial buffer if send is success
00348                     serial_buffer.clear();
00349 
00350                     // In P2P and Class B & C mode pending data will be sent automatically without uplink
00351                     if (peer_to_peer != true && _dot->getClass() == "A") {
00352                         if (_dot->getDataPending()) {
00353                             logDebug("Data is pending");
00354                             goto L_SEND;
00355                         }
00356                         if (_dot->getAckRequested()) {
00357                             logDebug("Ack requested");
00358                             goto L_SEND;
00359                         }
00360                         if (_dot->hasMacCommands()) {
00361                             logDebug("Pending MAC answers");
00362                             goto L_SEND;
00363                         }
00364                     }
00365                 }
00366             } else {
00367                 logDebug("Radio is busy, cannot send.\r\n");
00368                 ThisThread::sleep_for(10ms);
00369             }
00370 
00371         } else {
00372             logDebug("No data received from serial to send.\r\n");
00373             ThisThread::sleep_for(10ms);
00374         }
00375     }
00376 
00377     if (!_serialp->readable() && _dot->getStartUpMode() == mDot::SERIAL_MODE && !_serialp->escaped()) {
00378         sleep(true);
00379     }
00380 
00381     if (_serialp->escaped()) {
00382         _serialp->clearEscaped();
00383         _serialp->rxClear();
00384         serial_data_mode = false;
00385         _mode = mDot::COMMAND_MODE;
00386         logDebug("Exit Serial Mode");
00387         write(done);
00388         return;
00389     }
00390 
00391     if (!_dot->getNetworkJoinStatus()) {
00392         serial_data_mode = false;
00393         _mode = mDot::COMMAND_MODE;
00394         logDebug("Exit Serial Mode");
00395         write(no_carrier);
00396         return;
00397     }
00398 }
00399 
00400 bool CommandTerminal::autoJoinCheck() {
00401 
00402     std::string escape_buffer;
00403     int sleep = 1000;
00404     Timer tmr;
00405     tmr.start();
00406     int cnt = 0;
00407 
00408     while (!_dot->getNetworkJoinStatus()) {
00409         if (!serial_data_mode) {
00410             write("\r\nJoining network... ");
00411         }
00412         logInfo("Joining network... ");
00413 
00414         if (_dot->getNextTxMs() > 0) {
00415             if (!serial_data_mode) {
00416                 writef("\r\nWaiting %lu s before next join attempt\r\n", _dot->getNextTxMs() / 1000);
00417             }
00418             logInfo("Waiting %lu s before next join attempt", _dot->getNextTxMs() / 1000);
00419 
00420             tmr.reset();
00421             while (_dot->getNextTxMs() > 0 && !_serial.escaped()) {
00422                 osDelay(20);
00423             }
00424         }
00425 
00426         if (!_serial.escaped() && _dot->joinNetworkOnce() == mDot::MDOT_OK) {
00427             if (!serial_data_mode) {
00428                 write("Network Joined\r\n");
00429                 write(done);
00430             }
00431             logInfo("Network Joined");
00432             return false;
00433         }
00434 
00435         if (!serial_data_mode) {
00436             write("Network Join failed\r\n");
00437             write(error);
00438         }
00439         logInfo("Network Join failed");
00440 
00441         if (!_serial.escaped() && _dot->getFrequencySubBand() != 0 && _dot->getJoinRetries() > 0 && cnt++ > _dot->getJoinRetries()) {
00442             cnt = 0;
00443 
00444             if (lora::ChannelPlan::IsPlanFixed(_dot->getFrequencyBand())) {
00445                 uint8_t band = ((_dot->getFrequencySubBand()) % 8) + 1;
00446                 logWarning("Join retries exhausted, switching to sub band %u", band);
00447                 _dot->setFrequencySubBand(band);
00448             }
00449         }
00450 
00451         tmr.reset();
00452         while (std::chrono::duration_cast<std::chrono::milliseconds>(tmr.elapsed_time()).count() < sleep && !_serial.escaped()) {
00453             osDelay(10);
00454         }
00455 
00456         if (_serial.escaped()) {
00457             _serial.clearEscaped();
00458             serial_data_mode = false;
00459             _mode = mDot::COMMAND_MODE;
00460             if (!serial_data_mode) {
00461                 write("Join Canceled\r\n");
00462                 write(done);
00463             }
00464             logInfo("Join Canceled\r\n");
00465             return true;
00466         }
00467     }
00468 
00469     return false;
00470 }
00471 
00472 #define CMD_DEFS_COUNT  (13)
00473 #define CMD_DEFS_VARIANT_SIZE  (4)
00474 #define CMD_DEFS_LABEL_SIZE  (7)
00475 
00476 struct CommandDefinition {
00477     const char label[CMD_DEFS_LABEL_SIZE];
00478     const char var[CMD_DEFS_VARIANT_SIZE];
00479     uint8_t max_args;
00480     CommandFactory::CmdId_t id;
00481 };
00482 
00483 // Table of commands handled locally
00484 static const CommandDefinition cmd_defs[CMD_DEFS_COUNT] = {
00485     {"", "", 0, CommandFactory::eAT},
00486     {"E", "?01", 0, CommandFactory::eATE},
00487     {"V", "?01", 0, CommandFactory::eATVERBOSE},
00488     {"&K", "?03", 0, CommandFactory::eATK},
00489     {"+URC", "?", 1, CommandFactory::eURC},
00490     {"+LW", "?", 0, CommandFactory::eLW},
00491     {"+SD", "?", 0, CommandFactory::eSD},
00492     {"&WP", "", 0, CommandFactory::eATWP},
00493     {"&W", "", 0, CommandFactory::eATW},
00494     {"+SS", "", 0, CommandFactory::eSS},
00495     {"+DP", "?", 0, CommandFactory::eDP},
00496     {"+SLEEP", "", 1, CommandFactory::eSLEEP},
00497     {"+MEM", "?", 0, CommandFactory::eMEM}
00498 };
00499 
00500 
00501 
00502 void CommandTerminal::start() {
00503 
00504     char ch;
00505     bool running = true;
00506     bool echo = _dot->getEcho();
00507     std::string command;
00508 #if defined(MTS_DOT_COMMAND_HISTORY_FEATURE)
00509     std::deque<std::string> history;
00510     int history_index = -1;
00511 #endif
00512     std::vector<std::string> args;
00513     bool join_canceled = false;
00514 
00515     _autoOTAEnabled = _dot->getJoinMode() == mDot::AUTO_OTA;
00516 
00517     if (_dot->getStartUpMode() == mDot::SERIAL_MODE || CommandTerminal::Dot()->getTestModeEnabled()) {
00518 
00519         serial_data_mode = true;
00520         _mode = mDot::SERIAL_MODE;
00521 
00522         std::string escape_buffer;
00523         char ch;
00524 
00525         if (!_dot->getStandbyFlag()) {
00526             // wake up from power-on/reset
00527 
00528             int escape_timeout = 1000;
00529             Timer tmr;
00530             Timer escape_tmr;
00531 
00532             // wait one second for possible escape by user pressing '+' key
00533             tmr.reset();
00534             tmr.start();
00535             escape_tmr.reset();
00536             escape_tmr.start();
00537             while (std::chrono::duration_cast<std::chrono::milliseconds>(tmr.elapsed_time()).count() < escape_timeout) {
00538                 if (_serial.readable()) {
00539                     _serial.read(&ch, 1);
00540                     escape_buffer += ch;
00541                 }
00542 
00543                 if (escape_buffer.find("+") != std::string::npos) {
00544                     logInfo("Escape detected");
00545                     join_canceled = true;
00546                     serial_data_mode = false;
00547                     CommandTerminal::Dot()->setTestModeEnabled(false);
00548                     _mode = mDot::COMMAND_MODE;
00549                     command.clear();
00550                     break;
00551                 }
00552 
00553                 if (std::chrono::duration_cast<std::chrono::milliseconds>(escape_tmr.elapsed_time()).count() > escape_timeout)
00554                     escape_buffer.clear();
00555 
00556                 osDelay(1);
00557             }
00558         }
00559 
00560         if (_dot->getTestModeEnabled()) {
00561             CommandTerminal::Dot()->setAppNonce(0);
00562             Fota::getInstance()->enable(false);
00563         }
00564 
00565         if ((_mode == mDot::SERIAL_MODE || CommandTerminal::Dot()->getTestModeEnabled()) && !_dot->getNetworkJoinStatus() && _dot->getJoinMode() == mDot::OTA) {
00566             if (_dot->joinNetworkOnce() != mDot::MDOT_OK) {
00567                 serial_data_mode = false;
00568                 _mode = mDot::COMMAND_MODE;
00569 
00570                 logWarning("Start Up Mode set to SERIAL_MODE, but join failed.");
00571                 logWarning("Join Backoff until next join %lu ms", _dot->getNextTxMs());
00572                 _serial.writef("Network Not Joined\r\n");
00573                 _serial.writef(error);
00574             }
00575         }
00576     }
00577 
00578     if (CommandTerminal::Dot()->getTestModeEnabled() && _dot->getNetworkJoinStatus()) {
00579         while (CommandTerminal::Dot()->getTestModeEnabled() && !_events->PacketReceived) {
00580             std::vector<uint8_t> data;
00581             CommandTerminal::Dot()->send(data, false);
00582             osDelay(5000);
00583         }
00584     }
00585 
00586     if (_dot->getJoinMode() == mDot::PEER_TO_PEER) {
00587         peer_to_peer = true;
00588         Fota::getInstance()->enable(false);
00589         CommandTerminal::Dot()->clearMacCommands();
00590         CommandTerminal::Dot()->setTxDataRate(CommandTerminal::Dot()->getTxDataRate());
00591     } else {
00592         peer_to_peer = false;
00593     }
00594     //Run terminal session
00595     while (running) {
00596 
00597         if (_events != NULL && CommandTerminal::Dot()->getTestModeEnabled() && !_serial.escaped()) {
00598             if (_dot->getNextTxMs() > 0) {
00599                 osDelay(2000);
00600             } else if (!_dot->getNetworkJoinStatus() && _dot->getJoinMode() == mDot::OTA) {
00601                 if (_dot->joinNetworkOnce() != mDot::MDOT_OK) {
00602                     serial_data_mode = false;
00603                     _mode = mDot::COMMAND_MODE;
00604 
00605                     logWarning("Start Up Mode set to SERIAL_MODE, but join failed.");
00606                     _serial.writef("Network Not Joined\r\n");
00607                     _serial.writef(error);
00608                 }
00609             } else if (_events->PacketReceived) {
00610                 _events->handleTestModePacket();
00611                 _events->PacketReceived =  false;
00612 
00613             } else {
00614                 while (CommandTerminal::Dot()->getTestModeEnabled() && !_events->PacketReceived  && !_serial.escaped()) {
00615                     std::vector<uint8_t> data;
00616                     CommandTerminal::Dot()->send(data, false);
00617                     osDelay(5000);
00618                 }
00619             }
00620             continue;
00621         }
00622 
00623         // wait for input to reduce at command idle current
00624         while (!readable() || _mode == mDot::SERIAL_MODE) {
00625             if (!join_canceled && _autoOTAEnabled) {
00626                 join_canceled = autoJoinCheck();
00627                 if (join_canceled)
00628                     command.clear();
00629             }
00630 
00631             if (!_autoOTAEnabled || (!join_canceled && _autoOTAEnabled)) {
00632                 switch (_mode) {
00633                     case mDot::SERIAL_MODE:
00634                         // signal wakeup, read serial and output to radio
00635                         serialLoop();
00636                         continue;
00637                         break;
00638                     default:
00639                         break;
00640                 }
00641             }
00642 
00643             ch = '\0';
00644 
00645             if (_mode != mDot::SERIAL_MODE) {
00646                 ThisThread::sleep_for(10ms);
00647             }
00648             _serial.escaped();
00649         }
00650 
00651         // read characters
00652         if (readable()) {
00653             ch = read();
00654 
00655             if (ch == '\b' || ch == 0x7f) {
00656                 if (!command.empty()) {
00657                     writef("\b \b");
00658                     command.erase(command.size() - 1);
00659                 }
00660                 continue;
00661             } else if (ch == 0x1b || ch == 0x09) {
00662                 osDelay(20);
00663 
00664 #if defined(MTS_DOT_COMMAND_HISTORY_FEATURE)
00665                 // catch escape sequence, or tab
00666                 char ch1 = 0x00, ch2 = 0x00;
00667 
00668                 if (readable()) {
00669                     ch1 = read();
00670                     if (readable())
00671                         ch2 = read();
00672 
00673                     if (ch1 == 0x5b && ch2 == 0x41) {
00674                         // up key
00675                         for (size_t i = 0; i < command.size() + 1; i++) {
00676                             writef("\b \b");
00677                         }
00678                         if (history.size() > 0) {
00679                             if (++history_index >= int(history.size() - 1))
00680                                 history_index = history.size() - 1;
00681 
00682                             command = history[history_index];
00683 
00684                             if (command.size() > 128) {
00685                                 for (size_t i = 0; i < command.size(); i++) {
00686                                     writef("%c", command[i]);
00687                                 }
00688                             } else {
00689                                 writef("%s", command.c_str());
00690                             }
00691                         } else {
00692                             command.clear();
00693                         }
00694                     } else if (ch1 == 0x5b && ch2 == 0x42) {
00695 
00696                         // down key
00697                         for (size_t i = 0; i < command.size() + 1; i++) {
00698                             writef("\b \b");
00699                         }
00700 
00701                         if (--history_index < 0) {
00702                             history_index = -1;
00703                             command.clear();
00704                         } else {
00705                             command = history[history_index];
00706                             if (command.size() > 128) {
00707                                 for (size_t i = 0; i < command.size(); i++) {
00708                                     writef("%c", command[i]);
00709                                 }
00710 
00711                             } else {
00712                                 writef("%s", command.c_str());
00713                             }
00714                         }
00715                     }
00716                 }
00717 #endif
00718                 while (readable())
00719                     read();
00720                 continue;
00721             } else {
00722                 command += ch;
00723             }
00724 
00725             // echo chars if enabled
00726             if (echo && !(ch == '\r' || ch == '\n'))
00727                 writef("%c", ch);
00728         }
00729 
00730         // look for end of command line
00731         if (command.find("\n") != std::string::npos || command.find("\r") != std::string::npos) {
00732             // remove new line or cr character
00733             command.erase(command.size() - 1);
00734             write("\r"); // match standard modem output
00735             write(newline);
00736         } else {
00737             continue;
00738         }
00739 
00740         // trim whitespace from command
00741         mts::Text::trim(command, "\r\n\t ");
00742 
00743         if (command.size() < 1) {
00744             command.clear();
00745             continue;
00746         }
00747 
00748 #if defined(USE_PIN_PACKET_RX)
00749         _packet_rx_pin = GPIO_PIN_RESET;
00750 #endif
00751         command_processing = true;
00752 
00753         // parse command and args
00754         args.clear();
00755 
00756         // find first '=' character
00757         size_t delim_index = command.find("=");
00758         if (delim_index != std::string::npos) {
00759             args.push_back(command.substr(0, delim_index));
00760         } else {
00761             // find first ' ' character
00762             delim_index = command.find(" ");
00763             if (delim_index != std::string::npos) {
00764                 args.push_back(command.substr(0, delim_index));
00765             } else {
00766                 args.push_back(command);
00767             }
00768         }
00769 
00770         if (delim_index != std::string::npos) {
00771             std::vector<std::string> params = mts::Text::split(command.substr(delim_index + 1), ",");
00772             args.insert(args.end(), params.begin(), params.end());
00773         }
00774 
00775         args[0] = mts::Text::toUpper(args[0]);
00776         bool handled = false;
00777 
00778         // print help
00779         if ((args[0].find("?") == 0 || args[0].find("HELP") == 0) && args.size() == 1) {
00780 #if MTS_CMD_TERM_VERBOSE
00781             printHelp();
00782 #endif
00783             command.clear();
00784             handled = true;
00785         } else if (args[0].find("AT") == 0) {
00786             const CommandDefinition* def = NULL;    // Command to handle if matched
00787             char variant = '\0';                    // Variant character
00788             for (int d = 0; (d < CMD_DEFS_COUNT) && (def == NULL); ++d) {
00789                 size_t label_size = 2 + strlen(cmd_defs[d].label);
00790                 if (args[0].find(cmd_defs[d].label) == 2) {
00791                     // Label found following "AT"
00792                     for (int v = 0; v < CMD_DEFS_VARIANT_SIZE; ++v) {
00793                         if ((args[0][label_size] == cmd_defs[d].var[v]) &&
00794                             (args[0][label_size] == '\0' || args[0][label_size + 1] == '\0')) {
00795                             // Check for variant characters following label, this includes a NULL
00796                             def = &cmd_defs[d];
00797                             variant = cmd_defs[d].var[v];
00798                             break;
00799                         }
00800                     }
00801                 }
00802             }
00803             if (def != NULL) {
00804                 handled = true;
00805                 if (args.size() == 2 && args[1].length() == 1 && args[1][0] == '?') {
00806                     handled = false;
00807                 } else if (args.size() - 1 > def->max_args) {
00808 #if MTS_CMD_TERM_VERBOSE
00809                     write("Invalid argument\r\n");
00810 #endif
00811                     write(error);
00812                 } else {
00813                     switch (def->id) {
00814                         case CommandFactory::eAT:
00815                             write(done);
00816                             break;
00817                         case CommandFactory::eATE:
00818                             if (variant == '1' || variant == '0') {
00819                                 _dot->setEcho(variant == '1');
00820                                 echo = _dot->getEcho();
00821                             } else {
00822                                 writef("%d\r\n", _dot->getEcho());
00823                             }
00824                             write(done);
00825                             break;
00826                         case CommandFactory::eATVERBOSE:
00827                             if (variant == '1' || variant == '0') {
00828                                 _dot->setVerbose(variant == '1');
00829                             } else {
00830                                 writef("%d\r\n", _dot->getVerbose());
00831                             }
00832                             write(done);
00833                             break;
00834                         case CommandFactory::eATK:
00835                             if (variant == '3' || variant == '0') {
00836                                 _dot->setFlowControl(variant == '3');
00837                             } else {
00838                                 writef("%d\r\n", (_dot->getFlowControl() ? 3 : 0));
00839                             }
00840                             write(done);
00841                             break;
00842                         case CommandFactory::eURC:
00843                             if (args.size() == 1) {
00844                                 writef("%d\r\n", urc_enabled);
00845                                 write(done);
00846                             } else if (args[1].length() == 1) {
00847                                 if (args[1][0] != '?' && args[1][0] != '0' && args[1][0] != '1') {
00848 #if MTS_CMD_TERM_VERBOSE
00849                                     write("Invalid argument\r\n");
00850 #endif
00851                                     write(error);
00852                                 } else if (args[1][0] == '?') {
00853 #if MTS_CMD_TERM_VERBOSE
00854                                     write("(0:disable,1:enable)\r\n");
00855                                     write(done);
00856 #else
00857                                     write(error);
00858 #endif
00859                                 } else {
00860                                     urc_enabled = (args[1][0] == '1');
00861                                     write(done);
00862                                 }
00863                             } else {
00864                                 write(error);
00865                             }
00866                             break;
00867                         case CommandFactory::eLW:
00868                             writef("%s\r\n", _dot->getMACVersion());
00869                             write(done);
00870                             break;
00871                         case CommandFactory::eMEM:
00872                             free_mem();
00873                             write(done);
00874                             break;
00875                         case CommandFactory::eSD:
00876                             if (_dot->getNetworkJoinStatus()) {
00877                                 logDebug("Enter Serial Mode");
00878                                 write(connect);
00879                                 serial_data_mode = true;
00880                                 _serialp->clearEscaped();
00881                                 _mode = mDot::SERIAL_MODE;
00882                             } else {
00883                                 logDebug("Network Not Joined");
00884                                 write("Network Not Joined\r\n");
00885                                 write(error);
00886                             }
00887                             break;
00888                         case CommandFactory::eATW:
00889                             if (!_dot->saveConfig()) {
00890 #if MTS_CMD_TERM_VERBOSE
00891                                 write("Failed to save to flash");
00892 #endif
00893                                 write(error);
00894                             } else {
00895                                 write(done);
00896                             }
00897                             break;
00898 #if defined(TARGET_MTS_MDOT_F411RE) || defined(TARGET_XDOT_L151CC) || defined(TARGET_XDOT_MAX32670)
00899                         case CommandFactory::eATWP:
00900                             if (!_dot->saveProtectedConfig()) {
00901 #if MTS_CMD_TERM_VERBOSE
00902                                 write("Failed to save to flash");
00903 #endif
00904                                 write(error);
00905                             } else {
00906                                 write(done);
00907                             }
00908                             break;
00909 #endif
00910                         case CommandFactory::eSS:
00911                             _dot->saveNetworkSession();
00912                             write(done);
00913                             break;
00914                         case CommandFactory::eDP:
00915                             writef("%d\r\n",
00916                                 _dot->getDataPending() ||
00917                                 _dot->hasMacCommands() ||
00918                                 _dot->getAckRequested());
00919                             write(done);
00920                             break;
00921                         case CommandFactory::eSLEEP: {
00922                             bool temp_sleep_standby;
00923                             bool valid = false;
00924                             if ((args.size() > 1) && (args[1].length() == 1)) {
00925                                 if ((args[1][0] != '?' && args[1][0] != '0' && args[1][0] != '1')) {
00926 #if MTS_CMD_TERM_VERBOSE
00927                                     write("Invalid argument\r\n");
00928 #endif
00929                                     write(error);
00930                                 } else if (args[1][0] == '?') {
00931 #if MTS_CMD_TERM_VERBOSE
00932                                     write("(0:deepsleep,1:sleep)\r\n");
00933                                     write(done);
00934 #else
00935                                     write(error);
00936 #endif
00937                                 } else {
00938                                     valid = true;
00939                                     temp_sleep_standby = (args[1][0] == '0');
00940                                 }
00941                             } else if (args.size() == 1) {
00942                                 valid = true;
00943                                 temp_sleep_standby = _sleep_standby;
00944                             } else {
00945                                 write(error);
00946                             }
00947 
00948                             if (valid) {
00949                                 if (!_dot->getIsIdle()) {
00950                                     write("Dot is not idle\r\n");
00951                                     write(error);
00952                                 } else {
00953                                     _sleep_standby = temp_sleep_standby;
00954 #if defined(TARGET_MTS_MDOT_F411RE)
00955                                     //Read the board ID. If all 0's, it is revision B. This hardware does not support deep sleep.
00956                                     DigitalIn ID2(PC_4);
00957                                     DigitalIn ID1(PC_5);
00958                                     DigitalIn ID0(PD_2);
00959                                     if(ID2 == 0 && ID1 == 0 && ID0 == 0 && _sleep_standby == 1){
00960                                         _sleep_standby = 0;
00961                                         logWarning("This hardware version does not support deep sleep. Using sleep mode instead.");
00962                                     }
00963 #endif
00964                                     write(done);
00965                                     if (_dot->getBaud() < 9600)
00966                                         osDelay(20);
00967                                     else if (_dot->getBaud() > 57600)
00968                                         osDelay(2);
00969                                     else
00970                                         osDelay(5);
00971                                     this->sleep(_sleep_standby);
00972                                     osDelay(1);
00973                                 }
00974                             }
00975                             break;
00976                         }
00977                         default:
00978                             handled = false;
00979                             // Unhandled command
00980                             // Will only reach here if the table contains commands that do not have explicit cases
00981                             break;
00982                     }
00983                 }
00984 
00985 #if defined(MTS_DOT_ENABLE_LORAWAN_TESTMODE)
00986             } else if (args[0].find("AT+TM!") == 0 && args[0].length() == 6) {
00987                 handled = true;
00988                 if ((args.size() > 1) && (args[1].length() == 1)) {
00989                     if (args[1][0] == '0' || args[1][0] == '1') {
00990                         _dot->setTestModeEnabled(args[1][0] == '1');
00991                         _dot->saveConfig();
00992                         write(done);
00993                     } else {
00994                         write(error);
00995                     }
00996                 } else {
00997                     writef("%d\r\n", _dot->getTestModeEnabled());
00998                     write(done);
00999                 }
01000 #endif
01001             }
01002         }
01003 
01004         if (!handled) {
01005             bool found = false;
01006             bool query = false;
01007 
01008             std::string lookfor = args[0];
01009 
01010 #if MTS_CMD_TERM_VERBOSE
01011             // per command help
01012             if ((args[0].find("?") == 0 || args[0].find("HELP") == 0)) {
01013                 lookfor = mts::Text::toUpper(args[1]);
01014             }
01015 #endif
01016             // trim off any trailing '?' and mark as a query command
01017             if (args[0].rfind("?") == args[0].length() - 1) {
01018                 query = true;
01019                 lookfor = args[0].substr(0, args[0].length() - 1);
01020             }
01021 
01022             // search for command
01023             Command *cmd = NULL;
01024             for (int i = 0; i < CommandFactory::NUMBER_OF_CMDS; i++) {
01025                 cmd = CommandFactory::Create(static_cast<CommandFactory::CmdId_t>(i));
01026 
01027                 // match CMD or CMD? syntax if command is queryable
01028                 if (lookfor == cmd->text() && (!query || (query && cmd->queryable()))) {
01029                     found = true;
01030                     _errorMessage.clear();
01031 #if MTS_CMD_TERM_VERBOSE
01032                     if (args[0] == "HELP") {
01033                         writef("%s%s", cmd->help().c_str(), newline);
01034                         write(done);
01035                     }
01036                     else if (args.size() > 1 && args[1] == "?") {
01037                         writef("%s%s", cmd->usage().c_str(), newline);
01038                         write(done);
01039                     } else if (!cmd->verify(args)) {
01040 #else
01041                     if (args.size() > 1 && args[1] == "?") {
01042                         write(error);
01043                     } else if (!cmd->verify(args)) {
01044 #endif
01045                         if (!_errorMessage.empty()) {
01046                             writef("%s%s", _errorMessage.c_str(), newline);
01047                         }
01048                         write(error);
01049                     } else {
01050                         _errorMessage.clear();
01051                         if (cmd->action(args) == 0) {
01052                             writef("%s", done);
01053                         } else {
01054                             // Action was not successful
01055                             if (_errorMessage.empty()) {
01056                                 // If no error message was set, check for error recorded in mdot
01057                                 std::string dot_error = _dot->getLastError();
01058                                 if (!dot_error.empty()) {
01059                                     writef("%s%s", dot_error.c_str(), newline);
01060                                 }
01061                             } else {
01062                                 // Command set an error message
01063                                 writef("%s%s", _errorMessage.c_str(), newline);
01064                             }
01065                             write(error);
01066                         }
01067                     }
01068                 }
01069 
01070                 delete cmd;
01071 
01072                 if (found) {
01073                     break;
01074                 }
01075             }
01076 
01077             if (!found) {
01078                 write(command_error);
01079                 write(error);
01080             }
01081         }
01082 
01083 #if defined(USE_PIN_JOIN_STATUS)
01084         _join_status_pin = CommandTerminal::Dot()->getNetworkJoinStatus();
01085 #endif
01086         command_processing = false;
01087 
01088 
01089 #if defined(MTS_DOT_COMMAND_HISTORY_FEATURE)
01090         if (history.size() == 0 || history.front() != command)
01091             history.push_front(command);
01092         history_index = -1;
01093         command.clear();
01094 
01095         while (history.size() > 10)
01096             history.pop_back();
01097 #else
01098         command.clear();
01099 #endif
01100     }
01101 }
01102 
01103 void CommandTerminal::sleep(bool standby) {
01104 #if defined(USE_PIN_ON_SLEEP)
01105     _xbee_on_sleep = GPIO_PIN_RESET;
01106 #endif
01107     _serial.rxClear();
01108     _serial.txClear();
01109 
01110     LowPower::configExtGpios(_dot->getWakeMode(), _dot->getWakePin());
01111     _dot->sleep(_dot->getWakeInterval(), _dot->getWakeMode(), standby);
01112 
01113     _serial.rxClear();
01114     _serial.txClear();
01115 
01116     Fota::getInstance()->fixEventQueue();
01117 }
01118 
01119 std::string CommandTerminal::formatPacketData(const std::vector<uint8_t>& data, const uint8_t& format) {
01120     switch(format) {
01121         case(mDot::HEXADECIMAL):
01122             return mts::Text::bin2hexString(data);
01123 
01124         case(mDot::BINARY):
01125             return std::string(data.begin(), data.end());
01126 
01127         case(mDot::EXTENDED):
01128             return formatPacket(data, false);
01129 
01130         case(mDot::EXTENDED_HEX):
01131             return formatPacket(data, true);
01132 
01133         default:
01134             return "";
01135     }
01136 }
01137 
01138 bool CommandTerminal::waitForEscape(int timeout, mDot* dot, WaitType wait) {
01139     Timer timer;
01140 
01141     timer.start();
01142     while (std::chrono::duration_cast<std::chrono::milliseconds>(timer.elapsed_time()).count() < timeout) {
01143 
01144         if (dot != NULL) {
01145             if (wait == WAIT_SEND && (!dot->getIsTransmitting())) {
01146                 return false;
01147             }
01148         }
01149 
01150         if (_serialp != NULL && _serialp->escaped()) {
01151             _serialp->clearEscaped();
01152             return true;
01153         }
01154 
01155         ThisThread::sleep_for(10ms);
01156     }
01157 
01158     return false;
01159 }
01160 
01161 void CommandTerminal::wakeup(void) {
01162 }
01163 
01164 void CommandTerminal::RadioEvent::RxDone(uint8_t *payload, uint16_t size, int16_t rssi, int16_t snr, lora::DownlinkControl ctrl, uint8_t slot) {
01165     mDotEvent::RxDone(payload, size, rssi, snr, ctrl, slot);
01166     logDebug("RadioEvent - RxDone");
01167 }
01168 
01169 void CommandTerminal::RadioEvent::RxTimeout(uint8_t slot) {
01170     mDotEvent::RxTimeout(slot);
01171 #if defined(USE_PIN_PACKET_RX)
01172     _packet_rx_pin = GPIO_PIN_RESET;
01173 #endif
01174 }
01175 
01176 void CommandTerminal::RadioEvent::PacketRx(uint8_t port, uint8_t *payload, uint16_t size, int16_t rssi, int16_t snr, lora::DownlinkControl ctrl, uint8_t slot, uint8_t retries, uint32_t address, uint32_t fcnt, bool dupRx) {
01177     mDotEvent::PacketRx(port, payload, size, rssi, snr, ctrl, slot, retries, address, fcnt, dupRx);
01178     _rxAddress = address;
01179     _rxFcnt = fcnt;
01180 
01181     if(port == 200 || port == 201 || port == 202) {
01182         Fota::getInstance()->processCmd(payload, port, size);
01183 
01184         if (CommandTerminal::Dot()->getRxOutput() < mDot::EXTENDED) {
01185             return;
01186         }
01187     }
01188 #if defined(USE_PIN_PACKET_RX)
01189     _packet_rx_pin = GPIO_PIN_SET;
01190 #endif
01191 
01192     if (CommandTerminal::Dot()->getSettings()->Test.TestMode) {
01193         if (AckReceived || (PacketReceived && (RxPort != 0 || RxPayloadSize == 0))) {
01194             _testDownlinkCounter++;
01195             logDebug("Incremented downlink cnt %d", _testDownlinkCounter);
01196         } else {
01197             logDebug("PacketRx did not increment cnt port: %d size: %d", RxPort, RxPayloadSize);
01198             logDebug("PacketRx ACK: %d PKT: %d", AckReceived, PacketReceived);
01199         }
01200     }
01201 
01202     if (serial_data_mode && port != 0) {
01203         if (size > 0) {
01204             while (!CommandTerminal::Serial()->writeable()) ;
01205             if (CommandTerminal::Dot()->getRxOutput() >= mDot::EXTENDED) {
01206                 formatPacket(RxPayload, size, CommandTerminal::Dot()->getRxOutput()  == mDot::EXTENDED_HEX);
01207             } else {
01208                 CommandTerminal::Serial()->write((char*) RxPayload, RxPayloadSize);
01209             }
01210         }
01211         if (!CommandTerminal::Serial()->readable()
01212             && (_dot->getAckRequested()
01213                 || (peer_to_peer == false && _dot->hasMacCommands()))
01214             && (peer_to_peer || _dot->getClass() == "C" || _dot->getSettings()->Session.Class == lora::CLASS_B)) {
01215             _sendAck = true;
01216         }
01217     } else if (urc_enabled) {
01218         if (!command_processing) {
01219             while (!CommandTerminal::Serial()->writeable()) ;
01220             if (CommandTerminal::Dot()->getRxOutput() >= mDot::EXTENDED) {
01221                 formatPacket(RxPayload, size, CommandTerminal::Dot()->getRxOutput() == mDot::EXTENDED_HEX);
01222             } else {
01223                 CommandTerminal::Serial()->write("RECV\r\n", 6);
01224             }
01225         }
01226     }
01227 }
01228 
01229 void CommandTerminal::RadioEvent::handleTestModePacket() {
01230 #if defined(MTS_DOT_ENABLE_LORAWAN_TESTMODE)
01231     // static uint32_t last_rx_seq = 0;  // For Class B tests
01232     // bool start_test = false;
01233 
01234     std::string packet = mts::Text::bin2hexString(RxPayload, RxPayloadSize);
01235 
01236     CommandTerminal::Dot()->getSettings()->Test.TestMode = true;
01237     CommandTerminal::Dot()->setDisableIncrementDR(true);
01238 
01239     // last_rx_seq = CommandTerminal::Dot()->getSettings()->Session.UplinkCounter; // for class b tests
01240 
01241     uint32_t txPeriod = 5000;
01242     bool testConfirmed = false;
01243 
01244     std::string cls = "A";
01245 
01246     CommandTerminal::Dot()->setAck(0);
01247     CommandTerminal::Dot()->setAdr(true);
01248     CommandTerminal::Dot()->setAppPort(224);
01249 
01250     Timer sentTimer;
01251     sentTimer.start();
01252     while (CommandTerminal::Dot()->getSettings()->Test.TestMode) {
01253 TEST_START:
01254         // if (waitingForBeacon && BeaconLocked) {
01255         //     logInfo("send BeaconRxStatusInd");
01256         //     _data.clear();
01257         //     _data.push_back(0x40);
01258         //     for (size_t i = 0; i < sizeof(BeaconData); ++i) {
01259         //         _data.push_back(((uint8_t*)&BeaconData)[i]);
01260         //     }
01261         // } else
01262 
01263         if (RxPort == 224) {
01264             _data.clear();
01265 
01266             std::string packet = mts::Text::bin2hexString(RxPayload, RxPayloadSize);
01267             logDebug("Test Mode AppPort : %d", CommandTerminal::Dot()->getAppPort());
01268             logDebug("Test Mode Packet : %s", packet.c_str());
01269 
01270             switch (RxPayload[0]) {
01271                 case 0x00: { // PackageVersionReq
01272                     _data.push_back(0x00);
01273                     _data.push_back(0x06);
01274                     _data.push_back(0x01);
01275                     break;
01276                 }
01277                 case 0x01: { // DutResetReq
01278                     if (RxPayloadSize == 1) {
01279                         CommandTerminal::Dot()->resetCpu();
01280                     }
01281                     break;
01282                 }
01283                 case 0x02: { // DutJoinReq
01284                     if (RxPayloadSize == 1) {
01285                         while (CommandTerminal::Dot()->getNextTxMs() > 0) {
01286                             osDelay(1000);
01287                         }
01288                         CommandTerminal::Dot()->joinNetworkOnce();
01289                         return;
01290                     }
01291                     break;
01292                 }
01293                 case 0x03: { // SwitchClassReq
01294                     if (RxPayload[1] < 3) {
01295                         if (RxPayload[1] == 0x00) {
01296                             cls = "A";
01297                         } else {
01298                             cls = RxPayload[1] == 0x01 ? "B" : "C";
01299                         }
01300                     }
01301                     CommandTerminal::Dot()->setClass(cls);
01302                     break;
01303                 }
01304                 case 0x04: { // ADR Enabled/Disable
01305                     if (RxPayload[1] == 1)
01306                         CommandTerminal::Dot()->setAdr(true);
01307                     else
01308                         CommandTerminal::Dot()->setAdr(false);
01309                     break;
01310                 }
01311                 case 0x05: { // RegionalDutyCycleCtrlReq
01312                     if (RxPayload[1] == 0)
01313                         CommandTerminal::Dot()->setDisableDutyCycle(true);
01314                     else
01315                         CommandTerminal::Dot()->setDisableDutyCycle(false);
01316                     break;
01317                 }
01318                 case 0x06: { // TxPeriodicityChangeReq
01319                     if (RxPayload[1] < 2)
01320                         // 0, 1 => 5s
01321                         txPeriod = 5000U;
01322                     else if (RxPayload[1] < 8)
01323                         // 2 - 7 => 10s - 60s
01324                         txPeriod = (RxPayload[1] - 1) * 10000U;
01325                     else if (RxPayload[1] < 11) {
01326                         // 8, 9, 10 => 120s, 240s, 480s
01327                         txPeriod = 120 * (1 << (RxPayload[1] - 8)) * 1000U;
01328                     }
01329                     break;
01330                 }
01331                 case 0x07: { // TxFramesCtrl
01332                     if (RxPayload[1] == 0) {
01333                         // NO-OP
01334                     } else if (RxPayload[1] == 1) {
01335                         testConfirmed = false;
01336                         CommandTerminal::Dot()->getSettings()->Network.AckEnabled = 0;
01337                     } else if (RxPayload[1] == 2) {
01338                         testConfirmed = true;
01339                         // if ADR has set nbTrans then use the current setting
01340                         CommandTerminal::Dot()->getSettings()->Network.AckEnabled = 1;
01341                         if (CommandTerminal::Dot()->getSettings()->Session.Redundancy == 0) {
01342                             CommandTerminal::Dot()->getSettings()->Session.Redundancy = 1;
01343                         }
01344                     }
01345                     break;
01346                 }
01347                 case 0x08: { // EchoPayloadReq
01348                     _data.push_back(0x08);
01349                     for (size_t i = 1; i < RxPayloadSize; i++) {
01350                         _data.push_back(RxPayload[i] + 1);
01351                     }
01352                     break;
01353                 }
01354                 case 0x09: { // RxAppCntReq
01355                     _data.push_back(0x09);
01356                     _data.push_back(_testDownlinkCounter & 0xFF);
01357                     _data.push_back(_testDownlinkCounter >> 8);
01358                     break;
01359                 }
01360                 case 0x0A: { // RxAppCntResetReq
01361                     _testDownlinkCounter = 0;
01362                     break;
01363                 }
01364                 case 0x20: { // LinkCheckReq
01365                     CommandTerminal::Dot()->addMacCommand(lora::MOTE_MAC_LINK_CHECK_REQ, 0, 0);
01366                     break;
01367                 }
01368                 case 0x21: { // DeviceTimeReq
01369                     CommandTerminal::Dot()->addDeviceTimeRequest();
01370                     break;
01371                 }
01372                 case 0x22: { // PingSlotInfo
01373                     CommandTerminal::Dot()->setPingPeriodicity(RxPayload[1]);
01374                     CommandTerminal::Dot()->addMacCommand(lora::MOTE_MAC_PING_SLOT_INFO_REQ, RxPayload[1], 0);
01375                     break;
01376                 }
01377                 case 0x7D: { // TxCw
01378                     uint32_t freq = 0;
01379                     uint16_t timeout = 0;
01380                     uint8_t power = 0;
01381 
01382                     timeout = RxPayload[2] << 8 | RxPayload[1];
01383                     freq = (RxPayload[5] << 16 | RxPayload[4] << 8 | RxPayload[2]) * 100;
01384                     power = RxPayload[6];
01385 
01386                     CommandTerminal::Dot()->sendContinuous(true, timeout * 1000, freq, power);
01387                     break;
01388                 }
01389                 case 0x7E: { // DutFPort224DisableReq
01390                     _dot->setTestModeEnabled(false);
01391                     CommandTerminal::Dot()->getSettings()->Test.TestMode = false;
01392                     _dot->saveConfig();
01393                     CommandTerminal::Dot()->resetCpu();
01394                     break;
01395                 }
01396                 case 0x7F: { // DutVersionReq
01397                     std::string version = AT_APPLICATION_VERSION;
01398                     int temp = 0;
01399 
01400                     _data.push_back(0x7F);
01401                     sscanf(&version[0], "%d", &temp);
01402                     _data.push_back(temp); // AT_APP_VERSION_MAJOR; // MAJOR
01403                     sscanf(&version[2], "%d", &temp);
01404                     _data.push_back(temp); // AT_APP_VERSION_MINOR; // MINOR
01405                     sscanf(&version[4], "%d", &temp);
01406                     _data.push_back(temp); // AT_APP_VERSION_PATCH; // PATCH
01407                     if (version.size() > 7) {
01408                         sscanf(&version[6], "%d", &temp);
01409                         _data.push_back(temp); // AT_APP_VERSION_PATCH; // PATCH
01410                     } else {
01411                         _data.push_back(0); // AT_APP_VERSION_PATCH; // PATCH
01412                     }
01413                     version = LW_VERSION;
01414                     sscanf(&version[0], "%d", &temp);
01415                     _data.push_back(temp); // LW_VERSION; // MAJOR
01416                     sscanf(&version[2], "%d", &temp);
01417                     _data.push_back(temp); // LW_VERSION; // MINOR
01418                     sscanf(&version[4], "%d", &temp);
01419                     _data.push_back(temp); // LW_VERSION; // PATCH
01420                     if (version.size() > 7) {
01421                         sscanf(&version[6], "%d", &temp);
01422                         _data.push_back(temp); // LW_VERSION; // PATCH
01423                     } else {
01424                         _data.push_back(0); // LW_VERSION; // PATCH
01425                     }
01426                     version = RP_VERSION;
01427                     sscanf(&version[0], "%d", &temp);
01428                     _data.push_back(temp); // RP_VERSION; // MAJOR
01429                     sscanf(&version[2], "%d", &temp);
01430                     _data.push_back(temp); // RP_VERSION; // MINOR
01431                     sscanf(&version[4], "%d", &temp);
01432                     _data.push_back(temp); // RP_VERSION; // PATCH
01433                     if (version.size() > 7) {
01434                         sscanf(&version[6], "%d", &temp);
01435                         _data.push_back(temp); // RP_VERSION; // PATCH
01436                     } else {
01437                         _data.push_back(0); // RP_VERSION; // PATCH
01438                     }
01439 
01440 
01441                     break;
01442                 }
01443                 default: {
01444                     break;
01445                 }
01446             }
01447         }
01448 
01449         do {
01450             uint32_t loop_timer = 0;
01451             loop_timer = std::chrono::duration_cast<std::chrono::milliseconds>(sentTimer.elapsed_time()).count();
01452 
01453             PacketReceived = false;
01454             AckReceived = false;
01455 
01456             while (loop_timer < txPeriod || CommandTerminal::Dot()->getNextTxMs() > 0 || !CommandTerminal::Dot()->getIsIdle()) {
01457                 // if (waitingForBeacon && BeaconLocked) {
01458                 //     goto TEST_START;
01459                 // }
01460                 osDelay(500);
01461                 loop_timer = std::chrono::duration_cast<std::chrono::milliseconds>(sentTimer.elapsed_time()).count();
01462 
01463                 if (CommandTerminal::Dot()->getAckRequested() && loop_timer > 5000) {
01464                     break;
01465                 }
01466             }
01467 
01468             // packet may have been received while waiting, if ACK req then send uplink
01469             // else process data
01470             if (!PacketReceived || CommandTerminal::Dot()->getAckRequested()) {
01471 
01472                 sentTimer.reset();
01473 
01474                 if (_data.size() == 0) {
01475                     CommandTerminal::Dot()->setAppPort(1);
01476                     // if (cls == "B") {
01477                     //     // class B tests don't like empty packets
01478                         _data.push_back(0xFF);
01479                     // }
01480                 } else {
01481                     CommandTerminal::Dot()->setAppPort(224);
01482                 }
01483 
01484                 PacketReceived = false;
01485                 AckReceived = false;
01486 
01487                 if (CommandTerminal::Dot()->send(_data, testConfirmed) == mDot::MDOT_MAX_PAYLOAD_EXCEEDED) {
01488                     _data.clear();
01489                     RxPort = 0;
01490                     goto TEST_START;
01491                 }
01492 
01493                 // For Class B tests
01494                 // if (PacketReceived) {
01495                 //     last_rx_seq = CommandTerminal::Dot()->getSettings()->Session.UplinkCounter;
01496                 // }
01497 
01498                 _data.clear();
01499 
01500             }
01501 
01502         } while (!AckReceived && CommandTerminal::Dot()->recv(_data) != mDot::MDOT_OK);
01503 
01504 
01505         if (cls == "B" && !BeaconLocked) {
01506             CommandTerminal::Dot()->setClass(cls);
01507         }
01508     }
01509 #endif
01510 }
01511 
01512 uint8_t CommandTerminal::getBatteryLevel() {
01513     return _battery_level;
01514 }
01515 
01516 void CommandTerminal::setBatteryLevel(uint8_t battery_level) {
01517     _battery_level = battery_level;
01518 }
01519 
01520 void CommandTerminal::formatPacket(uint8_t* payload, uint16_t size, bool hex) {
01521 
01522     if(_dot->getAckRequested()) {
01523         f_data[0] = 0;
01524     } else {
01525         f_data[0] = 1;
01526     }
01527 
01528     f_data[1] = _rxAddress & 0xFF;
01529     f_data[2] = (_rxAddress >> 8) & 0xFF;
01530     f_data[3] = (_rxAddress >> 16) & 0xFF;
01531     f_data[4] = (_rxAddress >> 24) & 0xFF;
01532     f_data[5] = _rxFcnt & 0xFF;
01533     f_data[6] = (_rxFcnt >> 8) & 0xFF;
01534     f_data[7] = (_rxFcnt >> 16) & 0xFF;
01535     f_data[8] = (_rxFcnt >> 24) & 0xFF;
01536     f_data[9] = _events->RxPort;
01537 
01538     for(int i = 0; i < size; i++)
01539         f_data[i+10] = payload[i];
01540 
01541     if (hex) {
01542         std::string data = "RECV " + mts::Text::bin2hexString(f_data, size + 10) + "\r\n";
01543         CommandTerminal::Serial()->write(data.c_str(), data.size());
01544     } else {
01545         CommandTerminal::Serial()->write((char*)f_data, size + 10);
01546     }
01547 }
01548 
01549 
01550 std::string CommandTerminal::formatPacket(std::vector<uint8_t> payload, bool hex) {
01551 
01552     if(_dot->getAckRequested()) {
01553         f_data[0] = 0;
01554     } else {
01555         f_data[0] = 1;
01556     }
01557 
01558     f_data[1] = _rxAddress & 0xFF;
01559     f_data[2] = (_rxAddress >> 8) & 0xFF;
01560     f_data[3] = (_rxAddress >> 16) & 0xFF;
01561     f_data[4] = (_rxAddress >> 24) & 0xFF;
01562     f_data[5] = _rxFcnt & 0xFF;
01563     f_data[6] = (_rxFcnt >> 8) & 0xFF;
01564     f_data[7] = (_rxFcnt >> 16) & 0xFF;
01565     f_data[8] = (_rxFcnt >> 24) & 0xFF;
01566     f_data[9] = _events->RxPort;
01567 
01568     for(size_t i = 0; i < payload.size(); i++)
01569         f_data[i+10] = payload.at(i);
01570 
01571     if (hex) {
01572         return mts::Text::bin2hexString(f_data, payload.size() + 10);
01573     } else {
01574         return std::string((char*)f_data, payload.size() + 10);
01575     }
01576 
01577 }
01578 
01579 
01580 
01581 void CommandTerminal::formatPacketSDSend(std::vector<uint8_t> &payload) {
01582     if (_dot->getRxOutput() == mDot::EXTENDED_HEX) {
01583         int temp;
01584         std::vector<uint8_t> converted;
01585 
01586         for (size_t i=0; i < payload.size(); i+=2) {
01587             if (sscanf((char*)&payload[i], "%2x", &temp) == 1) {
01588                 converted.push_back((uint8_t)temp);
01589             }
01590         }
01591 
01592         payload = converted;
01593     }
01594 
01595     if(payload.size() >= 3) {
01596         _dot->setAppPort(payload[0]);
01597         _dot->setRepeat(0);
01598         _dot->setAck(0);
01599         switch(payload[1]) {
01600             case 0:
01601                 _dot->setAck(payload[2]);
01602                 break;
01603             case 1:
01604                 _dot->setRepeat(payload[2]);
01605                 break;
01606             default:
01607                 break;
01608         }
01609         payload.erase(payload.begin(), payload.begin()+3);
01610     }
01611 
01612 }
01613 
01614 
01615 
01616 CommandTerminal::~CommandTerminal() {
01617     delete _events;
01618 }