AT command firmware for MultiTech Dot devices.
Fork of mDot_AT_firmware by
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 }
Generated on Wed Dec 6 2023 19:34:05 by 1.7.2