Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Dependencies: libmDot-Custom MTS-Serial
Fork of mDot_AT_firmware_CUSTOM by
To change channel plans replace AS923 with AU915, EU868, KR920 or US915 on line 15
#define CHANNEL_PLAN CP_AS923
Diff: CommandTerminal/CommandTerminal.cpp
- Revision:
- 15:36db31c18231
- Parent:
- 12:98445fa50cd3
- Child:
- 18:4e02a7a01625
diff -r e80ace5a6834 -r 36db31c18231 CommandTerminal/CommandTerminal.cpp
--- a/CommandTerminal/CommandTerminal.cpp Mon Aug 29 10:05:41 2016 -0500
+++ b/CommandTerminal/CommandTerminal.cpp Wed Aug 31 11:57:27 2016 -0500
@@ -1,1083 +1,1123 @@
-#include "ctype.h"
-#include "CommandTerminal.h"
-#include "Command.h"
-#include "MTSLog.h"
-#include <cstdarg>
-#include <deque>
-#if defined(TARGET_XDOT_L151CC)
-#include "xdot_low_power.h"
-#endif
-
-#if defined(TARGET_MTS_MDOT_F411RE)
-const char CommandTerminal::banner[] = "\r\n\nMultiTech Systems LoRa XBee Module\r\n\n";
-#else
-const char CommandTerminal::banner[] = "\r\n\nMultiTech Systems LoRa xDot Module\r\n\n";
-#endif
-const char CommandTerminal::helpline[] = "Enter '?' for help\r\n";
-
-const char CommandTerminal::newline[] = "\r\n";
-
-// Command error text...
-const char CommandTerminal::command_error[] = "Command not found!\r\n";
-
-// Response texts...
-const char CommandTerminal::help[] = "\r\nHelp\r\n";
-const char CommandTerminal::cmd_error[] = "Invalid command\r\n";
-const char CommandTerminal::connect[] = "\r\nCONNECT\r\n";
-const char CommandTerminal::no_carrier[] = "\r\nNO CARRIER\r\n";
-const char CommandTerminal::done[] = "\r\nOK\r\n";
-const char CommandTerminal::error[] = "\r\nERROR\r\n";
-
-// Escape sequence...
-const char CommandTerminal::escape_sequence[] = "+++";
-
-mts::ATSerial* CommandTerminal::_serialp = NULL;
-mDot* CommandTerminal::_dot = NULL;
-
-CommandTerminal::RadioEvent* CommandTerminal::_events = new RadioEvent();
-
-static bool serial_data_mode = false;
-static bool peer_to_peer = false;
-
-std::string CommandTerminal::_errorMessage = "";
-
-void CommandTerminal::setErrorMessage(const char* message) {
- _errorMessage.assign(message);
-}
-
-void CommandTerminal::setErrorMessage(const std::string& message) {
- _errorMessage.assign(message);
-}
-
-const Command CommandTerminal::_commands[NO_OF_COMMANDS] = {
- CmdAttention(),
- CmdIdentification(),
- CmdResetCpu(),
- CmdDummy("Enable/Disable Echo", "ATE", "ATE0: disable, ATE1: enable", "(0,1)"),
- CmdDummy("Enable/Disable Verbose", "ATV", "ATV0: disable, ATV1: enable", "(0,1)"),
- CmdDummy("Hardware Flow Control", "AT&K", "AT&K0: disable, AT&K3: enable", "(0,3)"),
-
- CmdFactoryDefault(),
- CmdSaveConfig(),
- CmdDisplayConfig(),
- CmdDisplayStats(),
- CmdResetStats(),
- CmdSerialBaudRate(),
- CmdDebugBaudRate(),
- CmdStartUpMode(),
-
- CmdFrequencyBand(),
- CmdFrequencySubBand(),
- CmdPublicNetwork(),
- CmdDeviceId(),
- CmdDeviceClass(),
-
- CmdAppPort(),
- CmdNetworkAddress(),
- CmdNetworkSessionKey(),
- CmdDataSessionKey(),
- CmdUplinkCounter(),
- CmdDownlinkCounter(),
- CmdSaveSession(),
- CmdRestoreSession(),
- CmdNetworkKey(),
- CmdNetworkId(),
-
- CmdJoinDelay(),
- CmdJoinRequest(),
- CmdJoinRetries(),
- CmdJoinByteOrder(),
- CmdNetworkJoinMode(),
- CmdPreserveSession(),
- CmdNetworkJoinStatus(),
- CmdNetworkLinkCheck(),
- CmdLinkCheckCount(),
- CmdLinkCheckThreshold(),
- CmdEncryption(),
-
- CmdRssi(),
- CmdSnr(),
- CmdDataPending(),
-
- CmdSessionDataRate(),
- CmdChannelMask(),
-
- CmdTxDataRate(),
- CmdTxPower(),
- CmdAntennaGain(),
- CmdTxFrequency(),
- CmdTxInverted(),
- CmdTxWait(),
- CmdTxChannel(),
- CmdTxNextMs(),
- CmdTimeOnAir(),
-
- CmdRxDelay(),
- CmdRxOutput(),
- CmdRxInverted(),
-
- CmdErrorCorrection(),
- CmdCRC(),
- CmdAdaptiveDataRate(),
-
- CmdACKAttempts(),
- CmdRepeat(),
- CmdMacCmd(),
-
- CmdSendString(),
- CmdSendBinary(),
- CmdReceiveOnce(),
-
- CmdDummy("Serial Data Mode", "AT+SD", "Enter serial data mode, exit with '+++'", "NONE"),
- CmdDummy("Sleep Mode", "AT+SLEEP", "Enter sleep mode (0:deepsleep,1:sleep)", "(0,1)"),
- CmdSerialClearOnError(),
- CmdWakeMode(),
- CmdWakeInterval(),
- CmdWakePin(),
- CmdWakeDelay(),
- CmdWakeTimeout(),
- CmdPing(),
- CmdLogLevel(),
-
- CmdDummy("***** Test Commands *****", "", "", ""),
- CmdRxDataRate(),
- CmdRxFrequency(),
- CmdReceiveContinuous(),
- CmdSendStringOnInterval(),
-};
-
-const verify_ptr_t CommandTerminal::_verify[NO_OF_COMMANDS] = {
- CmdDummy::verify,
- CmdDummy::verify,
- CmdDummy::verify,
- CmdDummy::verify,
- CmdDummy::verify,
- CmdDummy::verify,
-
- CmdDummy::verify,
- CmdDummy::verify,
- CmdDummy::verify,
- CmdDummy::verify,
- CmdDummy::verify,
- CmdSerialBaudRate::verify,
- CmdDebugBaudRate::verify,
- CmdStartUpMode::verify,
-
- CmdFrequencyBand::verify,
- CmdFrequencySubBand::verify,
- CmdPublicNetwork::verify,
- CmdDeviceId::verify,
- CmdDeviceClass::verify,
-
- CmdAppPort::verify,
- CmdNetworkAddress::verify,
- CmdNetworkSessionKey::verify,
- CmdDataSessionKey::verify,
- CmdUplinkCounter::verify,
- CmdDownlinkCounter::verify,
- CmdDummy::verify,
- CmdDummy::verify,
- CmdNetworkKey::verify,
- CmdNetworkId::verify,
-
- CmdJoinDelay::verify,
- CmdJoinRequest::verify,
- CmdJoinRetries::verify,
- CmdJoinByteOrder::verify,
- CmdNetworkJoinMode::verify,
- CmdPreserveSession::verify,
- CmdDummy::verify,
- CmdDummy::verify,
- CmdLinkCheckCount::verify,
- CmdLinkCheckThreshold::verify,
- CmdEncryption::verify,
-
- CmdDummy::verify,
- CmdDummy::verify,
- CmdDummy::verify,
-
- CmdSessionDataRate::verify,
- CmdChannelMask::verify,
-
- CmdTxDataRate::verify,
- CmdTxPower::verify,
- CmdAntennaGain::verify,
- CmdTxFrequency::verify,
- CmdTxInverted::verify,
- CmdTxWait::verify,
- CmdTxChannel::verify,
- CmdTxNextMs::verify,
- CmdTimeOnAir::verify,
-
- CmdRxDelay::verify,
- CmdRxOutput::verify,
- CmdRxInverted::verify,
-
- CmdErrorCorrection::verify,
- CmdCRC::verify,
- CmdAdaptiveDataRate::verify,
-
- CmdACKAttempts::verify,
- CmdRepeat::verify,
- CmdMacCmd::verify,
-
- CmdSendString::verify,
- CmdSendBinary::verify,
- CmdReceiveOnce::verify,
-
- CmdDummy::verify,
- CmdDummy::verify,
- CmdSerialClearOnError::verify,
- CmdWakeMode::verify,
- CmdWakeInterval::verify,
- CmdWakePin::verify,
- CmdWakeDelay::verify,
- CmdWakeTimeout::verify,
- CmdDummy::verify,
- CmdLogLevel::verify,
-
- CmdDummy::verify,
- CmdRxDataRate::verify,
- CmdRxFrequency::verify,
- CmdReceiveContinuous::verify,
- CmdSendStringOnInterval::verify,
-};
-
-const action_ptr_t CommandTerminal::_action[NO_OF_COMMANDS] = {
- CmdAttention::action,
- CmdIdentification::action,
- CmdResetCpu::action,
- CmdDummy::action,
- CmdDummy::action,
- CmdDummy::action,
-
- CmdFactoryDefault::action,
- CmdSaveConfig::action,
- CmdDisplayConfig::action,
- CmdDisplayStats::action,
- CmdResetStats::action,
- CmdSerialBaudRate::action,
- CmdDebugBaudRate::action,
- CmdStartUpMode::action,
-
- CmdFrequencyBand::action,
- CmdFrequencySubBand::action,
- CmdPublicNetwork::action,
- CmdDeviceId::action,
- CmdDeviceClass::action,
-
- CmdAppPort::action,
- CmdNetworkAddress::action,
- CmdNetworkSessionKey::action,
- CmdDataSessionKey::action,
- CmdUplinkCounter::action,
- CmdDownlinkCounter::action,
- CmdSaveSession::action,
- CmdRestoreSession::action,
- CmdNetworkKey::action,
- CmdNetworkId::action,
-
- CmdJoinDelay::action,
- CmdJoinRequest::action,
- CmdJoinRetries::action,
- CmdJoinByteOrder::action,
- CmdNetworkJoinMode::action,
- CmdPreserveSession::action,
- CmdNetworkJoinStatus::action,
- CmdNetworkLinkCheck::action,
- CmdLinkCheckCount::action,
- CmdLinkCheckThreshold::action,
- CmdEncryption::action,
-
- CmdRssi::action,
- CmdSnr::action,
- CmdDataPending::action,
-
- CmdSessionDataRate::action,
- CmdChannelMask::action,
-
- CmdTxDataRate::action,
- CmdTxPower::action,
- CmdAntennaGain::action,
- CmdTxFrequency::action,
- CmdTxInverted::action,
- CmdTxWait::action,
- CmdTxChannel::action,
- CmdTxNextMs::action,
- CmdTimeOnAir::action,
-
- CmdRxDelay::action,
- CmdRxOutput::action,
- CmdRxInverted::action,
-
- CmdErrorCorrection::action,
- CmdCRC::action,
- CmdAdaptiveDataRate::action,
-
- CmdACKAttempts::action,
- CmdRepeat::action,
- CmdMacCmd::action,
-
- CmdSendString::action,
- CmdSendBinary::action,
- CmdReceiveOnce::action,
-
- CmdDummy::action,
- CmdDummy::action,
- CmdSerialClearOnError::action,
- CmdWakeMode::action,
- CmdWakeInterval::action,
- CmdWakePin::action,
- CmdWakeDelay::action,
- CmdWakeTimeout::action,
- CmdPing::action,
- CmdLogLevel::action,
-
- CmdDummy::action,
- CmdRxDataRate::action,
- CmdRxFrequency::action,
- CmdReceiveContinuous::action,
- CmdSendStringOnInterval::action,
-
-};
-
-CommandTerminal::CommandTerminal(mts::ATSerial& serial) :
- _serial(serial),
- _mode(mDot::COMMAND_MODE),
- _sleep_standby(true),
-#if defined(TARGET_MTS_MDOT_F411RE)
- _xbee_on_sleep(XBEE_ON_SLEEP),
-#else
- _xbee_on_sleep(GPIO2),
-#endif
- _autoOTAEnabled(false),
- _idle_thread(idle, NULL, osPriorityLow)
-{
- _serialp = &serial;
-}
-
-void CommandTerminal::init() {
- _dot->setEvents(_events);
-}
-
-void CommandTerminal::printHelp() {
- const char* name = NULL;
- const char* text = NULL;
- const char* desc = NULL;
- const char* tab = "\t";
-
- std::string header("Command");
- header.append(tab);
- header.append(tab);
- header.append("Name");
- header.append(tab);
- header.append(tab);
- header.append(tab);
- header.append("Description");
-
- write(newline);
- write(header.c_str());
- write(newline);
- write(newline);
-
- for (int i = 0; i < NO_OF_COMMANDS; i++) {
- name = _commands[i].name();
- text = _commands[i].text();
- desc = _commands[i].desc();
- write(text);
- if (strlen(text) < 8)
- write(tab);
- write(tab);
- write(name);
- if (strlen(name) < 8)
- write(tab);
- if (strlen(name) < 16)
- write(tab);
- write(tab);
- write(desc);
- write(newline);
- }
-
- write(newline);
-}
-
-bool CommandTerminal::writeable() {
- return _serialp->writeable();
-}
-
-bool CommandTerminal::readable() {
- return _serialp->readable();
-}
-
-char CommandTerminal::read() {
- char ch;
- _serialp->read(&ch, 1);
- return ch;
-}
-
-void CommandTerminal::write(const char* message) {
- while (!writeable())
- ;
- _serialp->write(message, strlen(message));
-}
-
-void CommandTerminal::writef(const char* format, ...) {
- char buff[256];
-
- va_list ap;
- va_start(ap, format);
- int size = vsnprintf(buff, 256, format, ap);
- while (!writeable())
- ;
- _serialp->write(buff, size);
- va_end(ap);
-}
-
-void CommandTerminal::serialLoop() {
- Timer serial_read_timer;
- std::vector<uint8_t> serial_buffer;
- std::vector<uint8_t> data;
- int timeout = 0;
-
- serial_read_timer.start();
-
- if (_dot->getStartUpMode() == mDot::SERIAL_MODE) {
- _xbee_on_sleep = GPIO_PIN_SET;
-
- timeout = _dot->getWakeDelay();
-
- // wait for timeout or start of serial data
- while (!readable() && serial_read_timer.read_ms() < timeout && !_serialp->escaped()) {
- osDelay(2);
- }
- }
-
- if (!readable() && _events->SendAck()) {
- logDebug("SERIAL NOT READABLE and ACK REQUESTED");
- goto L_SEND;
- }
-
- if (readable() && !_serialp->escaped()) {
-
- serial_read_timer.reset();
- timeout = _dot->getWakeTimeout();
-
- while (serial_read_timer.read_ms() < timeout && serial_buffer.size() <= _dot->getMaxPacketLength()) {
- while (readable() && serial_buffer.size() < _dot->getMaxPacketLength()) {
- serial_buffer.push_back(read());
- serial_read_timer.reset();
-
- if (_serialp->escaped())
- break;
- }
- }
-
- serial_read_timer.stop(), serial_read_timer.reset();
-
- if (!serial_buffer.empty()) {
- if (_dot->getStartUpMode() == mDot::SERIAL_MODE)
- _xbee_on_sleep = GPIO_PIN_RESET;
-
-L_SEND:
- // wait for any duty cycle limit to expire
- while (_dot->getNextTxMs() > 0 && !_serialp->escaped()) {
- osDelay(10);
- }
-
- if (_dot->getIsIdle()) {
- logDebug("Received serial data, sending out radio.");
-
- if (_dot->send(serial_buffer, false) != mDot::MDOT_OK) {
- logDebug("Send failed.");
- // If the data should be tossed after send failure, clear buffer
- if (_dot->getSerialClearOnError()) {
- serial_buffer.clear();
- }
- } else {
-
- // wait for send to finish
- while (!_dot->getIsIdle() && !_serialp->escaped()) {
- osDelay(10);
- }
-
- // call recv to wait for any packet before sending again
- if (!_serialp->escaped())
- _dot->recv(data);
-
- // Clear the serial buffer if send is success
- serial_buffer.clear();
-
- // In class C mode pending data will be sent automatically without uplink
- if (_dot->getClass() != "C") {
- if (_dot->getDataPending()) {
- logDebug("Data is pending");
- goto L_SEND;
- }
- if (_dot->getAckRequested()) {
- logDebug("Ack requested");
- goto L_SEND;
- }
- }
- }
- } else {
- logDebug("Radio is busy, cannot send.\r\n");
- osDelay(10);
- }
-
- } else {
- logDebug("No data received from serial to send.\r\n");
- }
- }
-
- if (!_serialp->readable() && _dot->getStartUpMode() == mDot::SERIAL_MODE && !_serialp->escaped()) {
- sleep(true);
- }
-
- if (_serialp->escaped()) {
- _serialp->clearEscaped();
- _serialp->rxClear();
- serial_data_mode = false;
- _mode = mDot::COMMAND_MODE;
- logDebug("Exit Serial Mode");
- write(done);
- return;
- }
-
- if (!_dot->getNetworkJoinStatus()) {
- serial_data_mode = false;
- _mode = mDot::COMMAND_MODE;
- logDebug("Exit Serial Mode");
- write(no_carrier);
- return;
- }
-}
-
-bool CommandTerminal::autoJoinCheck() {
-
- std::string escape_buffer;
- int sleep = 1000;
- Timer tmr;
- tmr.start();
- int cnt = 0;
-
- while (!_dot->getNetworkJoinStatus()) {
- if (!serial_data_mode) {
- write("\r\nJoining network... ");
- }
- logInfo("Joining network... ");
-
- if (_dot->getNextTxMs() > 0) {
- if (!serial_data_mode) {
- writef("\r\nWaiting %lu s before next join attempt\r\n", _dot->getNextTxMs() / 1000);
- }
- logInfo("Waiting %lu s before next join attempt", _dot->getNextTxMs() / 1000);
-
- tmr.reset();
- while (_dot->getNextTxMs() > 0 && !_serial.escaped()) {
- osDelay(20);
- }
- }
-
- if (!_serial.escaped() && _dot->joinNetworkOnce() == mDot::MDOT_OK) {
- if (!serial_data_mode) {
- write("Network Joined\r\n");
- write(done);
- }
- logInfo("Network Joined");
- return false;
- }
-
- if (!serial_data_mode) {
- write("Network Join failed\r\n");
- write(error);
- }
- logInfo("Network Join failed");
-
- if (!_serial.escaped() && _dot->getFrequencySubBand() != 0 && _dot->getJoinRetries() > 0 && cnt++ > _dot->getJoinRetries()) {
- cnt = 0;
-
- if (_dot->getFrequencyBand() == mDot::FB_US915 || _dot->getFrequencyBand() == mDot::FB_AU915 ) {
- uint8_t band = ((_dot->getFrequencySubBand()) % 8) + 1;
- logWarning("Join retries exhausted, switching to sub band %u", band);
- _dot->setFrequencySubBand(band);
- }
- }
-
- tmr.reset();
- while (tmr.read_ms() < sleep && !_serial.escaped()) {
- osDelay(10);
- }
-
- if (_serial.escaped()) {
- _serial.clearEscaped();
- serial_data_mode = false;
- _mode = mDot::COMMAND_MODE;
- if (!serial_data_mode) {
- write("Join Canceled\r\n");
- write(done);
- }
- logInfo("Join Canceled\r\n");
- return true;
- }
- }
-
- return false;
-}
-
-void CommandTerminal::start() {
-
- char ch;
- bool running = true;
- bool echo = _dot->getEcho();
- std::string command;
- std::deque<std::string> history;
- int history_index = -1;
- std::vector<std::string> args;
- bool join_canceled = false;
-
- _autoOTAEnabled = _dot->getJoinMode() == mDot::AUTO_OTA;
-
- if (_dot->getStartUpMode() == mDot::SERIAL_MODE) {
-
- serial_data_mode = true;
- _mode = mDot::SERIAL_MODE;
-
- std::string escape_buffer;
- char ch;
-
- if (!_dot->getStandbyFlag()) {
- // wake up from power-on/reset
-
- int escape_timeout = 1000;
- Timer tmr;
- Timer escape_tmr;
-
- // wait one second for possible escape by user pressing '+' key
- tmr.reset();
- tmr.start();
- escape_tmr.reset();
- escape_tmr.start();
- while (tmr.read_ms() < escape_timeout) {
- if (_serial.readable()) {
- _serial.read(&ch, 1);
- escape_buffer += ch;
- }
-
- if (escape_buffer.find("+") != std::string::npos) {
- logInfo("Escape detected");
- join_canceled = true;
- serial_data_mode = false;
- _mode = mDot::COMMAND_MODE;
- command.clear();
- break;
- }
-
- if (escape_tmr.read_ms() > escape_timeout)
- escape_buffer.clear();
-
- osDelay(1);
- }
- }
-
- if (_mode == mDot::SERIAL_MODE && !_dot->getNetworkJoinStatus() && _dot->getJoinMode() == mDot::OTA) {
- if (_dot->joinNetworkOnce() != mDot::MDOT_OK) {
- serial_data_mode = false;
- _mode = mDot::COMMAND_MODE;
-
- logWarning("Start Up Mode set to SERIAL_MODE, but join failed.");
- _serial.writef("Network Not Joined\r\n");
- _serial.writef(error);
- }
- }
- }
-
- if (_dot->getJoinMode() == mDot::PEER_TO_PEER) {
- peer_to_peer = true;
- } else {
- peer_to_peer = false;
- }
-
- //Run terminal session
- while (running) {
-
- // wait for input to reduce at command idle current
- while (!readable() || _mode == mDot::SERIAL_MODE) {
- if (!join_canceled && _autoOTAEnabled) {
- join_canceled = autoJoinCheck();
- if (join_canceled)
- command.clear();
- }
-
- if (!_autoOTAEnabled || (!join_canceled && _autoOTAEnabled)) {
- switch (_mode) {
- case mDot::SERIAL_MODE:
- // signal wakeup, read serial and output to radio
- serialLoop();
- continue;
- break;
- default:
- break;
- }
- }
-
- ch = '\0';
-
- wait(0.00001); // 10 us
- _serial.escaped();
- }
-
- // read characters
- if (readable()) {
- ch = read();
-
- if (ch == '\b' || ch == 0x7f) {
- if (!command.empty()) {
- writef("\b \b");
- command.erase(command.size() - 1);
- }
- continue;
- } else if (ch == 0x1b || ch == 0x09) {
- osDelay(20);
- // catch escape sequence, or tab
- char ch1 = 0x00, ch2 = 0x00;
-
- if (readable()) {
- ch1 = read();
- if (readable())
- ch2 = read();
-
- if (ch1 == 0x5b && ch2 == 0x41) {
- // up key
- for (size_t i = 0; i < command.size() + 1; i++) {
- writef("\b \b");
- }
- if (history.size() > 0) {
- if (++history_index >= int(history.size() - 1))
- history_index = history.size() - 1;
-
- command = history[history_index];
- writef("%s", history[history_index].c_str());
- } else {
- command.clear();
- }
- } else if (ch1 == 0x5b && ch2 == 0x42) {
-
- // down key
- for (size_t i = 0; i < command.size() + 1; i++) {
- writef("\b \b");
- }
-
- if (--history_index < 0) {
- history_index = -1;
- command.clear();
- } else {
- command = history[history_index];
- writef("%s", history[history_index].c_str());
- }
- }
- }
- while (readable())
- read();
- continue;
- } else {
- command += ch;
- }
-
- // echo chars if enabled
- if (echo && !(ch == '\r' || ch == '\n'))
- writef("%c", ch);
- }
-
- // look for end of command line
- if (command.find("\n") != std::string::npos || command.find("\r") != std::string::npos) {
- // remove new line or cr character
- command.erase(command.size() - 1);
- write("\r"); // match standard modem output
- write(newline);
- } else {
- continue;
- }
-
- // trim whitespace from command
- mts::Text::trim(command, "\r\n\t ");
-
- if (command.size() < 1) {
- command.clear();
- continue;
- }
-
- // parse command and args
- args.clear();
-
- // find first '=' character
- size_t delim_index = command.find("=");
- if (delim_index != std::string::npos) {
- args.push_back(command.substr(0, delim_index));
- } else {
- // find first ' ' character
- delim_index = command.find(" ");
- if (delim_index != std::string::npos) {
- args.push_back(command.substr(0, delim_index));
- } else {
- args.push_back(command);
- }
- }
-
- if (delim_index != std::string::npos) {
- std::vector<std::string> params = mts::Text::split(command.substr(delim_index + 1), ",");
- args.insert(args.end(), params.begin(), params.end());
- }
-
- args[0] = mts::Text::toUpper(args[0]);
-
- // print help
- if ((args[0].find("?") == 0 || args[0].find("HELP") == 0) && args.size() == 1) {
- printHelp();
- command.clear();
- } else if ((args[0].find("ATE?") == 0 && args[0].length() == 4) || (args[0].find("ATE") == 0 && args[0].length() == 3)) {
- writef("%d\r\n", _dot->getEcho());
- write(done);
- } else if (args[0].find("ATE0") == 0 && args[0].length() == 4) {
- _dot->setEcho(false);
- write(done);
- echo = _dot->getEcho();
- } else if (args[0].find("ATE1") == 0 && args[0].length() == 4) {
- _dot->setEcho(true);
- write(done);
- echo = _dot->getEcho();
- } else if ((args[0].find("ATV?") == 0 && args[0].length() == 4) || (args[0].find("ATV") == 0 && args[0].length() == 3)) {
- writef("%d\r\n", _dot->getVerbose());
- write(done);
- } else if (args[0].find("ATV0") == 0 && args[0].length() == 4) {
- _dot->setVerbose(false);
- write(done);
- } else if (args[0].find("ATV1") == 0 && args[0].length() == 4) {
- _dot->setVerbose(true);
- write(done);
- } else if ((args[0].find("AT&K?") == 0 && args[0].length() == 5) || (args[0].find("AT&K") == 0 && args[0].length() == 4)) {
- writef("%d\r\n", (_dot->getFlowControl() ? 3 : 0));
- write(done);
- } else if (args[0].find("AT&K0") == 0 && args[0].length() == 5) {
- _dot->setFlowControl(false);
- write(done);
- } else if (args[0].find("AT&K3") == 0 && args[0].length() == 5) {
- _dot->setFlowControl(true);
- write(done);
- } else if (args[0] == "AT+SD") {
- if (_dot->getNetworkJoinStatus()) {
- logDebug("Enter Serial Mode");
- write(connect);
- serial_data_mode = true;
- _mode = mDot::SERIAL_MODE;
- } else {
- logDebug("Network Not Joined");
- write("Network Not Joined\r\n");
- write(error);
- }
- } else if (args[0] == "AT+SLEEP") {
- if (args.size() > 2 && (args[1] != "?")) {
- write("Invalid argument\r\n");
- write(error);
- } else {
- if (args.size() > 1 && args[1] == "?") {
- write("(0:deepsleep,1:sleep)\r\n");
- write(done);
- } else {
- _sleep_standby = !(args.size() > 1 && args[1] == "1");
- write(done);
- this->sleep(_sleep_standby);
- wait(0.1);
- }
- }
- } else {
- bool found = false;
- bool query = false;
-
- std::string lookfor = args[0];
-
- // per command help
- if ((args[0].find("?") == 0 || args[0].find("HELP") == 0))
- lookfor = mts::Text::toUpper(args[1]);
-
- // trim off any trailing '?' and mark as a query command
- if (args[0].rfind("?") == args[0].length() - 1) {
- query = true;
- lookfor = args[0].substr(0, args[0].length() - 1);
- }
-
- // search for command
- for (int i = 0; i < NO_OF_COMMANDS; i++) {
-
- // match CMD or CMD? syntax if command is queryable
- if (lookfor == _commands[i].text() && (!query || (query && _commands[i].queryable()))) {
- found = true;
- if (args[0] == "HELP") {
- writef("%s%s", _commands[i].help().c_str(), newline);
- write(done);
- }
-
- else if (args.size() > 1 && args[1] == "?") {
- writef("%s%s", _commands[i].usage().c_str(), newline);
- write(done);
- } else if (!_verify[i](args)) {
- writef("%s%s", _errorMessage.c_str(), newline);
- writef("%s", error);
- } else {
- if (_action[i](args) == 0) {
- writef("%s", done);
- } else {
- writef("%s%s", _errorMessage.c_str(), newline);
- writef("%s", error);
- }
- }
- }
- }
-
- if (!found) {
- writef("%s", command_error);
- writef("%s", error);
- }
- }
-
- if (history.size() == 0 || history.front() != command)
- history.push_front(command);
- history_index = -1;
- command.clear();
-
- while (history.size() > 10)
- history.pop_back();
- }
-}
-
-void CommandTerminal::sleep(bool standby) {
- _xbee_on_sleep = GPIO_PIN_RESET;
-
- _serial.rxClear();
- _serial.txClear();
-
-#if defined(TARGET_XDOT_L151CC)
- xdot_save_gpio_state();
-
- /* GPIO Ports Clock Enable */
- __GPIOA_CLK_ENABLE();
- __GPIOB_CLK_ENABLE();
- __GPIOC_CLK_ENABLE();
- __GPIOH_CLK_ENABLE();
-
- GPIO_InitTypeDef GPIO_InitStruct;
-
- // UART1_TX, UART1_RTS & UART1_CTS to analog nopull - RX could be a wakeup source
- GPIO_InitStruct.Pin = GPIO_PIN_9 | GPIO_PIN_11 | GPIO_PIN_12;
- GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
- GPIO_InitStruct.Pull = GPIO_NOPULL;
- HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
-
- // I2C_SDA & I2C_SCL to analog nopull
- GPIO_InitStruct.Pin = GPIO_PIN_8 | GPIO_PIN_9;
- GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
- GPIO_InitStruct.Pull = GPIO_NOPULL;
- HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
-
- // SPI_MOSI, SPI_MISO, SPI_SCK, & SPI_NSS to analog nopull
- GPIO_InitStruct.Pin = GPIO_PIN_12 | GPIO_PIN_13 | GPIO_PIN_14 | GPIO_PIN_15;
- GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
- GPIO_InitStruct.Pull = GPIO_NOPULL;
- HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
-
- // iterate through potential wake pins - leave the configured wake pin alone if one is needed
- if (_dot->getWakePin() != WAKE || _dot->getWakeMode() == mDot::RTC_ALARM) {
- GPIO_InitStruct.Pin = GPIO_PIN_0;
- GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
- GPIO_InitStruct.Pull = GPIO_NOPULL;
- HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
- }
- if (_dot->getWakePin() != GPIO0 || _dot->getWakeMode() == mDot::RTC_ALARM) {
- GPIO_InitStruct.Pin = GPIO_PIN_4;
- GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
- GPIO_InitStruct.Pull = GPIO_NOPULL;
- HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
- }
- if (_dot->getWakePin() != GPIO1 || _dot->getWakeMode() == mDot::RTC_ALARM) {
- GPIO_InitStruct.Pin = GPIO_PIN_5;
- GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
- GPIO_InitStruct.Pull = GPIO_NOPULL;
- HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
- }
- if (_dot->getWakePin() != GPIO2 || _dot->getWakeMode() == mDot::RTC_ALARM) {
- GPIO_InitStruct.Pin = GPIO_PIN_0;
- GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
- GPIO_InitStruct.Pull = GPIO_NOPULL;
- HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
- }
- if (_dot->getWakePin() != GPIO3 || _dot->getWakeMode() == mDot::RTC_ALARM) {
- GPIO_InitStruct.Pin = GPIO_PIN_2;
- GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
- GPIO_InitStruct.Pull = GPIO_NOPULL;
- HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
- }
- if (_dot->getWakePin() != UART1_RX || _dot->getWakeMode() == mDot::RTC_ALARM) {
- GPIO_InitStruct.Pin = GPIO_PIN_10;
- GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
- GPIO_InitStruct.Pull = GPIO_NOPULL;
- HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
- }
-#endif
-
- _dot->sleep(_dot->getWakeInterval(), _dot->getWakeMode(), standby);
-
-#if defined(TARGET_XDOT_L151CC)
- xdot_restore_gpio_state();
-#endif
-}
-
-std::string CommandTerminal::formatPacketData(const std::vector<uint8_t>& data, const uint8_t& format) {
- if (format == mDot::HEXADECIMAL)
- return mts::Text::bin2hexString(data);
- else
- return std::string(data.begin(), data.end());
-}
-
-bool CommandTerminal::waitForEscape(int timeout, mDot* dot, WaitType wait) {
- Timer timer;
-
- timer.start();
- while (timer.read_ms() < timeout) {
-
- if (dot != NULL) {
- if (wait == WAIT_SEND && (!dot->getIsTransmitting())) {
- return false;
- }
- }
-
- if (_serialp != NULL && _serialp->escaped()) {
- _serialp->clearEscaped();
- return true;
- }
-
- osDelay(10);
- }
-
- return false;
-}
-
-void CommandTerminal::wakeup(void) {
-}
-
-void CommandTerminal::RadioEvent::PacketRx(uint8_t port, uint8_t *payload, uint16_t size, int16_t rssi, int8_t snr, lora::DownlinkControl ctrl, uint8_t slot, uint8_t retries) {
- mDotEvent::PacketRx(port, payload, size, rssi, snr, ctrl, retries);
-
- if (serial_data_mode) {
- logDebug("Rx %d bytes", size);
- if (size > 0) {
- CommandTerminal::Serial()->write((char*) RxPayload, RxPayloadSize);
- }
- if (!CommandTerminal::Serial()->readable() && _dot->getAckRequested() && _dot->getClass() == "C") {
- _sendAck = true;
- }
- }
-}
-
-CommandTerminal::~CommandTerminal() {
- delete _events;
-}
-
+#include "ctype.h"
+#include "CommandTerminal.h"
+#include "Command.h"
+#include "MTSLog.h"
+#include <cstdarg>
+#include <deque>
+#if defined(TARGET_XDOT_L151CC)
+#include "xdot_low_power.h"
+#endif
+
+#if defined(TARGET_MTS_MDOT_F411RE)
+const char CommandTerminal::banner[] = "\r\n\nMultiTech Systems LoRa XBee Module\r\n\n";
+#else
+const char CommandTerminal::banner[] = "\r\n\nMultiTech Systems LoRa xDot Module\r\n\n";
+#endif
+const char CommandTerminal::helpline[] = "Enter '?' for help\r\n";
+
+const char CommandTerminal::newline[] = "\r\n";
+
+// Command error text...
+const char CommandTerminal::command_error[] = "Command not found!\r\n";
+
+// Response texts...
+const char CommandTerminal::help[] = "\r\nHelp\r\n";
+const char CommandTerminal::cmd_error[] = "Invalid command\r\n";
+const char CommandTerminal::connect[] = "\r\nCONNECT\r\n";
+const char CommandTerminal::no_carrier[] = "\r\nNO CARRIER\r\n";
+const char CommandTerminal::done[] = "\r\nOK\r\n";
+const char CommandTerminal::error[] = "\r\nERROR\r\n";
+
+// Escape sequence...
+const char CommandTerminal::escape_sequence[] = "+++";
+
+mts::ATSerial* CommandTerminal::_serialp = NULL;
+mDot* CommandTerminal::_dot = NULL;
+
+CommandTerminal::RadioEvent* CommandTerminal::_events = new RadioEvent();
+
+static bool serial_data_mode = false;
+static bool peer_to_peer = false;
+
+std::string CommandTerminal::_errorMessage = "";
+
+void CommandTerminal::setErrorMessage(const char* message) {
+ _errorMessage.assign(message);
+}
+
+void CommandTerminal::setErrorMessage(const std::string& message) {
+ _errorMessage.assign(message);
+}
+
+const Command CommandTerminal::_commands[NO_OF_COMMANDS] = {
+ CmdAttention(),
+ CmdIdentification(),
+ CmdResetCpu(),
+ CmdDummy("Enable/Disable Echo", "ATE", "ATE0: disable, ATE1: enable", "(0,1)"),
+ CmdDummy("Enable/Disable Verbose", "ATV", "ATV0: disable, ATV1: enable", "(0,1)"),
+ CmdDummy("Hardware Flow Control", "AT&K", "AT&K0: disable, AT&K3: enable", "(0,3)"),
+
+ CmdFactoryDefault(),
+ CmdSaveConfig(),
+ CmdDisplayConfig(),
+ CmdDisplayStats(),
+ CmdResetStats(),
+ CmdSerialBaudRate(),
+ CmdDebugBaudRate(),
+ CmdStartUpMode(),
+
+ CmdFrequencyBand(),
+ CmdFrequencySubBand(),
+ CmdPublicNetwork(),
+ CmdDeviceId(),
+ CmdDeviceClass(),
+
+ CmdAppPort(),
+ CmdNetworkAddress(),
+ CmdNetworkSessionKey(),
+ CmdDataSessionKey(),
+ CmdUplinkCounter(),
+ CmdDownlinkCounter(),
+ CmdSaveSession(),
+ CmdRestoreSession(),
+ CmdNetworkKey(),
+ CmdNetworkId(),
+
+ CmdJoinDelay(),
+// Remove join settings commands until valid case for changing default settings
+// CmdJoinRx1Offset(),
+// CmdJoinRx2Datarate(),
+// CmdJoinRx2Frequency(),
+ CmdJoinRequest(),
+ CmdJoinRetries(),
+ CmdJoinByteOrder(),
+ CmdNetworkJoinMode(),
+ CmdPreserveSession(),
+ CmdNetworkJoinStatus(),
+ CmdNetworkLinkCheck(),
+ CmdLinkCheckCount(),
+ CmdLinkCheckThreshold(),
+ CmdEncryption(),
+
+ CmdRssi(),
+ CmdSnr(),
+ CmdDataPending(),
+
+ CmdSessionDataRate(),
+ CmdChannelMask(),
+
+ CmdTxDataRate(),
+ CmdTxPower(),
+ CmdAntennaGain(),
+ CmdTxFrequency(),
+ CmdTxInverted(),
+ CmdTxWait(),
+ CmdTxChannel(),
+ CmdTxNextMs(),
+ CmdTimeOnAir(),
+
+ CmdRxDelay(),
+ CmdRxOutput(),
+ CmdRxInverted(),
+
+ CmdErrorCorrection(),
+ CmdCRC(),
+ CmdAdaptiveDataRate(),
+
+ CmdACKAttempts(),
+ CmdRepeat(),
+ CmdMacCmd(),
+
+ CmdSendString(),
+ CmdSendBinary(),
+ CmdReceiveOnce(),
+
+ CmdDummy("Serial Data Mode", "AT+SD", "Enter serial data mode, exit with '+++'", "NONE"),
+ CmdDummy("Sleep Mode", "AT+SLEEP", "Enter sleep mode (0:deepsleep,1:sleep)", "(0,1)"),
+ CmdSerialClearOnError(),
+ CmdWakeMode(),
+ CmdWakeInterval(),
+ CmdWakePin(),
+ CmdWakeDelay(),
+ CmdWakeTimeout(),
+ CmdPing(),
+ CmdLogLevel(),
+
+ CmdDummy("***** Test Commands *****", "", "", ""),
+ CmdRxDataRate(),
+ CmdRxFrequency(),
+ CmdReceiveContinuous(),
+ CmdSendStringOnInterval(),
+
+#ifdef MTS_RADIO_DEBUG_COMMANDS
+ CmdDummy("***** Debug Commands *****", "", "", ""),
+ CmdSendContinuous(),
+ CmdWriteProtectedConfig(),
+ CmdDumpRegisters(),
+ CmdEraseFlash(),
+ CmdDisableDutyCycle(),
+#endif
+
+};
+
+const verify_ptr_t CommandTerminal::_verify[NO_OF_COMMANDS] = {
+ CmdDummy::verify,
+ CmdDummy::verify,
+ CmdDummy::verify,
+ CmdDummy::verify,
+ CmdDummy::verify,
+ CmdDummy::verify,
+
+ CmdDummy::verify,
+ CmdDummy::verify,
+ CmdDummy::verify,
+ CmdDummy::verify,
+ CmdDummy::verify,
+ CmdSerialBaudRate::verify,
+ CmdDebugBaudRate::verify,
+ CmdStartUpMode::verify,
+
+ CmdFrequencyBand::verify,
+ CmdFrequencySubBand::verify,
+ CmdPublicNetwork::verify,
+ CmdDeviceId::verify,
+ CmdDeviceClass::verify,
+
+ CmdAppPort::verify,
+ CmdNetworkAddress::verify,
+ CmdNetworkSessionKey::verify,
+ CmdDataSessionKey::verify,
+ CmdUplinkCounter::verify,
+ CmdDownlinkCounter::verify,
+ CmdDummy::verify,
+ CmdDummy::verify,
+ CmdNetworkKey::verify,
+ CmdNetworkId::verify,
+
+ CmdJoinDelay::verify,
+// Remove join settings commands until valid case for changing default settings
+// CmdJoinRx1Offset::verify,
+// CmdJoinRx2Datarate::verify,
+// CmdJoinRx2Frequency::verify,
+ CmdJoinRequest::verify,
+ CmdJoinRetries::verify,
+ CmdJoinByteOrder::verify,
+ CmdNetworkJoinMode::verify,
+ CmdPreserveSession::verify,
+ CmdDummy::verify,
+ CmdDummy::verify,
+ CmdLinkCheckCount::verify,
+ CmdLinkCheckThreshold::verify,
+ CmdEncryption::verify,
+
+ CmdDummy::verify,
+ CmdDummy::verify,
+ CmdDummy::verify,
+
+ CmdSessionDataRate::verify,
+ CmdChannelMask::verify,
+
+ CmdTxDataRate::verify,
+ CmdTxPower::verify,
+ CmdAntennaGain::verify,
+ CmdTxFrequency::verify,
+ CmdTxInverted::verify,
+ CmdTxWait::verify,
+ CmdTxChannel::verify,
+ CmdTxNextMs::verify,
+ CmdTimeOnAir::verify,
+
+ CmdRxDelay::verify,
+ CmdRxOutput::verify,
+ CmdRxInverted::verify,
+
+ CmdErrorCorrection::verify,
+ CmdCRC::verify,
+ CmdAdaptiveDataRate::verify,
+
+ CmdACKAttempts::verify,
+ CmdRepeat::verify,
+ CmdMacCmd::verify,
+
+ CmdSendString::verify,
+ CmdSendBinary::verify,
+ CmdReceiveOnce::verify,
+
+ CmdDummy::verify,
+ CmdDummy::verify,
+ CmdSerialClearOnError::verify,
+ CmdWakeMode::verify,
+ CmdWakeInterval::verify,
+ CmdWakePin::verify,
+ CmdWakeDelay::verify,
+ CmdWakeTimeout::verify,
+ CmdDummy::verify,
+ CmdLogLevel::verify,
+
+ CmdDummy::verify,
+ CmdRxDataRate::verify,
+ CmdRxFrequency::verify,
+ CmdReceiveContinuous::verify,
+ CmdSendStringOnInterval::verify,
+
+#ifdef MTS_RADIO_DEBUG_COMMANDS
+ CmdDummy::verify,
+ CmdSendContinuous::verify,
+ CmdDummy::verify,
+ CmdDummy::verify,
+ CmdEraseFlash::verify,
+ CmdDisableDutyCycle::verify,
+#endif
+
+};
+
+const action_ptr_t CommandTerminal::_action[NO_OF_COMMANDS] = {
+ CmdAttention::action,
+ CmdIdentification::action,
+ CmdResetCpu::action,
+ CmdDummy::action,
+ CmdDummy::action,
+ CmdDummy::action,
+
+ CmdFactoryDefault::action,
+ CmdSaveConfig::action,
+ CmdDisplayConfig::action,
+ CmdDisplayStats::action,
+ CmdResetStats::action,
+ CmdSerialBaudRate::action,
+ CmdDebugBaudRate::action,
+ CmdStartUpMode::action,
+
+ CmdFrequencyBand::action,
+ CmdFrequencySubBand::action,
+ CmdPublicNetwork::action,
+ CmdDeviceId::action,
+ CmdDeviceClass::action,
+
+ CmdAppPort::action,
+ CmdNetworkAddress::action,
+ CmdNetworkSessionKey::action,
+ CmdDataSessionKey::action,
+ CmdUplinkCounter::action,
+ CmdDownlinkCounter::action,
+ CmdSaveSession::action,
+ CmdRestoreSession::action,
+ CmdNetworkKey::action,
+ CmdNetworkId::action,
+
+ CmdJoinDelay::action,
+// Remove join settings commands until valid case for changing default settings
+// CmdJoinRx1Offset::action,
+// CmdJoinRx2Datarate::action,
+// CmdJoinRx2Frequency::action,
+ CmdJoinRequest::action,
+ CmdJoinRetries::action,
+ CmdJoinByteOrder::action,
+ CmdNetworkJoinMode::action,
+ CmdPreserveSession::action,
+ CmdNetworkJoinStatus::action,
+ CmdNetworkLinkCheck::action,
+ CmdLinkCheckCount::action,
+ CmdLinkCheckThreshold::action,
+ CmdEncryption::action,
+
+ CmdRssi::action,
+ CmdSnr::action,
+ CmdDataPending::action,
+
+ CmdSessionDataRate::action,
+ CmdChannelMask::action,
+
+ CmdTxDataRate::action,
+ CmdTxPower::action,
+ CmdAntennaGain::action,
+ CmdTxFrequency::action,
+ CmdTxInverted::action,
+ CmdTxWait::action,
+ CmdTxChannel::action,
+ CmdTxNextMs::action,
+ CmdTimeOnAir::action,
+
+ CmdRxDelay::action,
+ CmdRxOutput::action,
+ CmdRxInverted::action,
+
+ CmdErrorCorrection::action,
+ CmdCRC::action,
+ CmdAdaptiveDataRate::action,
+
+ CmdACKAttempts::action,
+ CmdRepeat::action,
+ CmdMacCmd::action,
+
+ CmdSendString::action,
+ CmdSendBinary::action,
+ CmdReceiveOnce::action,
+
+ CmdDummy::action,
+ CmdDummy::action,
+ CmdSerialClearOnError::action,
+ CmdWakeMode::action,
+ CmdWakeInterval::action,
+ CmdWakePin::action,
+ CmdWakeDelay::action,
+ CmdWakeTimeout::action,
+ CmdPing::action,
+ CmdLogLevel::action,
+
+ CmdDummy::action,
+ CmdRxDataRate::action,
+ CmdRxFrequency::action,
+ CmdReceiveContinuous::action,
+ CmdSendStringOnInterval::action,
+
+#ifdef MTS_RADIO_DEBUG_COMMANDS
+ CmdDummy::action,
+ CmdSendContinuous::action,
+ CmdWriteProtectedConfig::action,
+ CmdDumpRegisters::action,
+ CmdEraseFlash::action,
+ CmdDisableDutyCycle::action,
+#endif
+
+};
+
+CommandTerminal::CommandTerminal(mts::ATSerial& serial) :
+ _serial(serial),
+ _mode(mDot::COMMAND_MODE),
+ _sleep_standby(true),
+#if defined(TARGET_MTS_MDOT_F411RE)
+ _xbee_on_sleep(XBEE_ON_SLEEP),
+#else
+ _xbee_on_sleep(GPIO2),
+#endif
+ _autoOTAEnabled(false),
+ _idle_thread(idle, NULL, osPriorityLow)
+{
+ _serialp = &serial;
+}
+
+void CommandTerminal::init() {
+ _dot->setEvents(_events);
+}
+
+void CommandTerminal::printHelp() {
+ const char* name = NULL;
+ const char* text = NULL;
+ const char* desc = NULL;
+ const char* tab = "\t";
+
+ std::string header("Command");
+ header.append(tab);
+ header.append(tab);
+ header.append("Name");
+ header.append(tab);
+ header.append(tab);
+ header.append(tab);
+ header.append("Description");
+
+ write(newline);
+ write(header.c_str());
+ write(newline);
+ write(newline);
+
+ for (int i = 0; i < NO_OF_COMMANDS; i++) {
+ name = _commands[i].name();
+ text = _commands[i].text();
+ desc = _commands[i].desc();
+ write(text);
+ if (strlen(text) < 8)
+ write(tab);
+ write(tab);
+ write(name);
+ if (strlen(name) < 8)
+ write(tab);
+ if (strlen(name) < 16)
+ write(tab);
+ write(tab);
+ write(desc);
+ write(newline);
+ }
+
+ write(newline);
+}
+
+bool CommandTerminal::writeable() {
+ return _serialp->writeable();
+}
+
+bool CommandTerminal::readable() {
+ return _serialp->readable();
+}
+
+char CommandTerminal::read() {
+ char ch;
+ _serialp->read(&ch, 1);
+ return ch;
+}
+
+void CommandTerminal::write(const char* message) {
+ while (!writeable())
+ ;
+ _serialp->write(message, strlen(message));
+}
+
+void CommandTerminal::writef(const char* format, ...) {
+ char buff[256];
+
+ va_list ap;
+ va_start(ap, format);
+ int size = vsnprintf(buff, 256, format, ap);
+ while (!writeable())
+ ;
+ _serialp->write(buff, size);
+ va_end(ap);
+}
+
+void CommandTerminal::serialLoop() {
+ Timer serial_read_timer;
+ std::vector<uint8_t> serial_buffer;
+ std::vector<uint8_t> data;
+ int timeout = 0;
+
+ serial_read_timer.start();
+
+ if (_dot->getStartUpMode() == mDot::SERIAL_MODE) {
+ _xbee_on_sleep = GPIO_PIN_SET;
+
+ timeout = _dot->getWakeDelay();
+
+ // wait for timeout or start of serial data
+ while (!readable() && serial_read_timer.read_ms() < timeout && !_serialp->escaped()) {
+ osDelay(2);
+ }
+ }
+
+ if (!readable() && _events->SendAck()) {
+ logDebug("SERIAL NOT READABLE and ACK REQUESTED");
+ goto L_SEND;
+ }
+
+ if (readable() && !_serialp->escaped()) {
+
+ serial_read_timer.reset();
+ timeout = _dot->getWakeTimeout();
+
+ while (serial_read_timer.read_ms() < timeout && serial_buffer.size() <= _dot->getMaxPacketLength()) {
+ while (readable() && serial_buffer.size() < _dot->getMaxPacketLength()) {
+ serial_buffer.push_back(read());
+ serial_read_timer.reset();
+
+ if (_serialp->escaped())
+ break;
+ }
+ }
+
+ serial_read_timer.stop(), serial_read_timer.reset();
+
+ if (!serial_buffer.empty()) {
+ if (_dot->getStartUpMode() == mDot::SERIAL_MODE)
+ _xbee_on_sleep = GPIO_PIN_RESET;
+
+L_SEND:
+ // wait for any duty cycle limit to expire
+ while (_dot->getNextTxMs() > 0 && !_serialp->escaped()) {
+ osDelay(10);
+ }
+
+ if (_dot->getIsIdle()) {
+ logDebug("Received serial data, sending out radio.");
+
+ if (_dot->send(serial_buffer, false) != mDot::MDOT_OK) {
+ logDebug("Send failed.");
+ // If the data should be tossed after send failure, clear buffer
+ if (_dot->getSerialClearOnError()) {
+ serial_buffer.clear();
+ }
+ } else {
+
+ // wait for send to finish
+ while (!_dot->getIsIdle() && !_serialp->escaped()) {
+ osDelay(10);
+ }
+
+ // call recv to wait for any packet before sending again
+ if (!_serialp->escaped())
+ _dot->recv(data);
+
+ // Clear the serial buffer if send is success
+ serial_buffer.clear();
+
+ // In class C mode pending data will be sent automatically without uplink
+ if (_dot->getClass() != "C") {
+ if (_dot->getDataPending()) {
+ logDebug("Data is pending");
+ goto L_SEND;
+ }
+ if (_dot->getAckRequested()) {
+ logDebug("Ack requested");
+ goto L_SEND;
+ }
+ }
+ }
+ } else {
+ logDebug("Radio is busy, cannot send.\r\n");
+ osDelay(10);
+ }
+
+ } else {
+ logDebug("No data received from serial to send.\r\n");
+ }
+ }
+
+ if (!_serialp->readable() && _dot->getStartUpMode() == mDot::SERIAL_MODE && !_serialp->escaped()) {
+ sleep(true);
+ }
+
+ if (_serialp->escaped()) {
+ _serialp->clearEscaped();
+ _serialp->rxClear();
+ serial_data_mode = false;
+ _mode = mDot::COMMAND_MODE;
+ logDebug("Exit Serial Mode");
+ write(done);
+ return;
+ }
+
+ if (!_dot->getNetworkJoinStatus()) {
+ serial_data_mode = false;
+ _mode = mDot::COMMAND_MODE;
+ logDebug("Exit Serial Mode");
+ write(no_carrier);
+ return;
+ }
+}
+
+bool CommandTerminal::autoJoinCheck() {
+
+ std::string escape_buffer;
+ int sleep = 1000;
+ Timer tmr;
+ tmr.start();
+ int cnt = 0;
+
+ while (!_dot->getNetworkJoinStatus()) {
+ if (!serial_data_mode) {
+ write("\r\nJoining network... ");
+ }
+ logInfo("Joining network... ");
+
+ if (_dot->getNextTxMs() > 0) {
+ if (!serial_data_mode) {
+ writef("\r\nWaiting %lu s before next join attempt\r\n", _dot->getNextTxMs() / 1000);
+ }
+ logInfo("Waiting %lu s before next join attempt", _dot->getNextTxMs() / 1000);
+
+ tmr.reset();
+ while (_dot->getNextTxMs() > 0 && !_serial.escaped()) {
+ osDelay(20);
+ }
+ }
+
+ if (!_serial.escaped() && _dot->joinNetworkOnce() == mDot::MDOT_OK) {
+ if (!serial_data_mode) {
+ write("Network Joined\r\n");
+ write(done);
+ }
+ logInfo("Network Joined");
+ return false;
+ }
+
+ if (!serial_data_mode) {
+ write("Network Join failed\r\n");
+ write(error);
+ }
+ logInfo("Network Join failed");
+
+ if (!_serial.escaped() && _dot->getFrequencySubBand() != 0 && _dot->getJoinRetries() > 0 && cnt++ > _dot->getJoinRetries()) {
+ cnt = 0;
+
+ if (_dot->getFrequencyBand() == mDot::FIXED ) {
+ uint8_t band = ((_dot->getFrequencySubBand()) % 8) + 1;
+ logWarning("Join retries exhausted, switching to sub band %u", band);
+ _dot->setFrequencySubBand(band);
+ }
+ }
+
+ tmr.reset();
+ while (tmr.read_ms() < sleep && !_serial.escaped()) {
+ osDelay(10);
+ }
+
+ if (_serial.escaped()) {
+ _serial.clearEscaped();
+ serial_data_mode = false;
+ _mode = mDot::COMMAND_MODE;
+ if (!serial_data_mode) {
+ write("Join Canceled\r\n");
+ write(done);
+ }
+ logInfo("Join Canceled\r\n");
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void CommandTerminal::start() {
+
+ char ch;
+ bool running = true;
+ bool echo = _dot->getEcho();
+ std::string command;
+ std::deque<std::string> history;
+ int history_index = -1;
+ std::vector<std::string> args;
+ bool join_canceled = false;
+
+ _autoOTAEnabled = _dot->getJoinMode() == mDot::AUTO_OTA;
+
+ if (_dot->getStartUpMode() == mDot::SERIAL_MODE) {
+
+ serial_data_mode = true;
+ _mode = mDot::SERIAL_MODE;
+
+ std::string escape_buffer;
+ char ch;
+
+ if (!_dot->getStandbyFlag()) {
+ // wake up from power-on/reset
+
+ int escape_timeout = 1000;
+ Timer tmr;
+ Timer escape_tmr;
+
+ // wait one second for possible escape by user pressing '+' key
+ tmr.reset();
+ tmr.start();
+ escape_tmr.reset();
+ escape_tmr.start();
+ while (tmr.read_ms() < escape_timeout) {
+ if (_serial.readable()) {
+ _serial.read(&ch, 1);
+ escape_buffer += ch;
+ }
+
+ if (escape_buffer.find("+") != std::string::npos) {
+ logInfo("Escape detected");
+ join_canceled = true;
+ serial_data_mode = false;
+ _mode = mDot::COMMAND_MODE;
+ command.clear();
+ break;
+ }
+
+ if (escape_tmr.read_ms() > escape_timeout)
+ escape_buffer.clear();
+
+ osDelay(1);
+ }
+ }
+
+ if (_mode == mDot::SERIAL_MODE && !_dot->getNetworkJoinStatus() && _dot->getJoinMode() == mDot::OTA) {
+ if (_dot->joinNetworkOnce() != mDot::MDOT_OK) {
+ serial_data_mode = false;
+ _mode = mDot::COMMAND_MODE;
+
+ logWarning("Start Up Mode set to SERIAL_MODE, but join failed.");
+ _serial.writef("Network Not Joined\r\n");
+ _serial.writef(error);
+ }
+ }
+ }
+
+ if (_dot->getJoinMode() == mDot::PEER_TO_PEER) {
+ peer_to_peer = true;
+ } else {
+ peer_to_peer = false;
+ }
+
+ //Run terminal session
+ while (running) {
+
+ // wait for input to reduce at command idle current
+ while (!readable() || _mode == mDot::SERIAL_MODE) {
+ if (!join_canceled && _autoOTAEnabled) {
+ join_canceled = autoJoinCheck();
+ if (join_canceled)
+ command.clear();
+ }
+
+ if (!_autoOTAEnabled || (!join_canceled && _autoOTAEnabled)) {
+ switch (_mode) {
+ case mDot::SERIAL_MODE:
+ // signal wakeup, read serial and output to radio
+ serialLoop();
+ continue;
+ break;
+ default:
+ break;
+ }
+ }
+
+ ch = '\0';
+
+ wait(0.00001); // 10 us
+ _serial.escaped();
+ }
+
+ // read characters
+ if (readable()) {
+ ch = read();
+
+ if (ch == '\b' || ch == 0x7f) {
+ if (!command.empty()) {
+ writef("\b \b");
+ command.erase(command.size() - 1);
+ }
+ continue;
+ } else if (ch == 0x1b || ch == 0x09) {
+ osDelay(20);
+ // catch escape sequence, or tab
+ char ch1 = 0x00, ch2 = 0x00;
+
+ if (readable()) {
+ ch1 = read();
+ if (readable())
+ ch2 = read();
+
+ if (ch1 == 0x5b && ch2 == 0x41) {
+ // up key
+ for (size_t i = 0; i < command.size() + 1; i++) {
+ writef("\b \b");
+ }
+ if (history.size() > 0) {
+ if (++history_index >= int(history.size() - 1))
+ history_index = history.size() - 1;
+
+ command = history[history_index];
+ writef("%s", history[history_index].c_str());
+ } else {
+ command.clear();
+ }
+ } else if (ch1 == 0x5b && ch2 == 0x42) {
+
+ // down key
+ for (size_t i = 0; i < command.size() + 1; i++) {
+ writef("\b \b");
+ }
+
+ if (--history_index < 0) {
+ history_index = -1;
+ command.clear();
+ } else {
+ command = history[history_index];
+ writef("%s", history[history_index].c_str());
+ }
+ }
+ }
+ while (readable())
+ read();
+ continue;
+ } else {
+ command += ch;
+ }
+
+ // echo chars if enabled
+ if (echo && !(ch == '\r' || ch == '\n'))
+ writef("%c", ch);
+ }
+
+ // look for end of command line
+ if (command.find("\n") != std::string::npos || command.find("\r") != std::string::npos) {
+ // remove new line or cr character
+ command.erase(command.size() - 1);
+ write("\r"); // match standard modem output
+ write(newline);
+ } else {
+ continue;
+ }
+
+ // trim whitespace from command
+ mts::Text::trim(command, "\r\n\t ");
+
+ if (command.size() < 1) {
+ command.clear();
+ continue;
+ }
+
+ // parse command and args
+ args.clear();
+
+ // find first '=' character
+ size_t delim_index = command.find("=");
+ if (delim_index != std::string::npos) {
+ args.push_back(command.substr(0, delim_index));
+ } else {
+ // find first ' ' character
+ delim_index = command.find(" ");
+ if (delim_index != std::string::npos) {
+ args.push_back(command.substr(0, delim_index));
+ } else {
+ args.push_back(command);
+ }
+ }
+
+ if (delim_index != std::string::npos) {
+ std::vector<std::string> params = mts::Text::split(command.substr(delim_index + 1), ",");
+ args.insert(args.end(), params.begin(), params.end());
+ }
+
+ args[0] = mts::Text::toUpper(args[0]);
+
+ // print help
+ if ((args[0].find("?") == 0 || args[0].find("HELP") == 0) && args.size() == 1) {
+ printHelp();
+ command.clear();
+ } else if ((args[0].find("ATE?") == 0 && args[0].length() == 4) || (args[0].find("ATE") == 0 && args[0].length() == 3)) {
+ writef("%d\r\n", _dot->getEcho());
+ write(done);
+ } else if (args[0].find("ATE0") == 0 && args[0].length() == 4) {
+ _dot->setEcho(false);
+ write(done);
+ echo = _dot->getEcho();
+ } else if (args[0].find("ATE1") == 0 && args[0].length() == 4) {
+ _dot->setEcho(true);
+ write(done);
+ echo = _dot->getEcho();
+ } else if ((args[0].find("ATV?") == 0 && args[0].length() == 4) || (args[0].find("ATV") == 0 && args[0].length() == 3)) {
+ writef("%d\r\n", _dot->getVerbose());
+ write(done);
+ } else if (args[0].find("ATV0") == 0 && args[0].length() == 4) {
+ _dot->setVerbose(false);
+ write(done);
+ } else if (args[0].find("ATV1") == 0 && args[0].length() == 4) {
+ _dot->setVerbose(true);
+ write(done);
+ } else if ((args[0].find("AT&K?") == 0 && args[0].length() == 5) || (args[0].find("AT&K") == 0 && args[0].length() == 4)) {
+ writef("%d\r\n", (_dot->getFlowControl() ? 3 : 0));
+ write(done);
+ } else if (args[0].find("AT&K0") == 0 && args[0].length() == 5) {
+ _dot->setFlowControl(false);
+ write(done);
+ } else if (args[0].find("AT&K3") == 0 && args[0].length() == 5) {
+ _dot->setFlowControl(true);
+ write(done);
+ } else if (args[0] == "AT+SD") {
+ if (_dot->getNetworkJoinStatus()) {
+ logDebug("Enter Serial Mode");
+ write(connect);
+ serial_data_mode = true;
+ _mode = mDot::SERIAL_MODE;
+ } else {
+ logDebug("Network Not Joined");
+ write("Network Not Joined\r\n");
+ write(error);
+ }
+ } else if (args[0] == "AT+SLEEP") {
+ if (args.size() > 2 && (args[1] != "?")) {
+ write("Invalid argument\r\n");
+ write(error);
+ } else {
+ if (args.size() > 1 && args[1] == "?") {
+ write("(0:deepsleep,1:sleep)\r\n");
+ write(done);
+ } else {
+ _sleep_standby = !(args.size() > 1 && args[1] == "1");
+ write(done);
+ this->sleep(_sleep_standby);
+ wait(0.1);
+ }
+ }
+ } else {
+ bool found = false;
+ bool query = false;
+
+ std::string lookfor = args[0];
+
+ // per command help
+ if ((args[0].find("?") == 0 || args[0].find("HELP") == 0))
+ lookfor = mts::Text::toUpper(args[1]);
+
+ // trim off any trailing '?' and mark as a query command
+ if (args[0].rfind("?") == args[0].length() - 1) {
+ query = true;
+ lookfor = args[0].substr(0, args[0].length() - 1);
+ }
+
+ // search for command
+ for (int i = 0; i < NO_OF_COMMANDS; i++) {
+
+ // match CMD or CMD? syntax if command is queryable
+ if (lookfor == _commands[i].text() && (!query || (query && _commands[i].queryable()))) {
+ found = true;
+ if (args[0] == "HELP") {
+ writef("%s%s", _commands[i].help().c_str(), newline);
+ write(done);
+ }
+
+ else if (args.size() > 1 && args[1] == "?") {
+ writef("%s%s", _commands[i].usage().c_str(), newline);
+ write(done);
+ } else if (!_verify[i](args)) {
+ writef("%s%s", _errorMessage.c_str(), newline);
+ writef("%s", error);
+ } else {
+ if (_action[i](args) == 0) {
+ writef("%s", done);
+ } else {
+ writef("%s%s", _errorMessage.c_str(), newline);
+ writef("%s", error);
+ }
+ }
+ }
+ }
+
+ if (!found) {
+ writef("%s", command_error);
+ writef("%s", error);
+ }
+ }
+
+ if (history.size() == 0 || history.front() != command)
+ history.push_front(command);
+ history_index = -1;
+ command.clear();
+
+ while (history.size() > 10)
+ history.pop_back();
+ }
+}
+
+void CommandTerminal::sleep(bool standby) {
+ _xbee_on_sleep = GPIO_PIN_RESET;
+
+ _serial.rxClear();
+ _serial.txClear();
+
+#if defined(TARGET_XDOT_L151CC)
+ xdot_save_gpio_state();
+
+ /* GPIO Ports Clock Enable */
+ __GPIOA_CLK_ENABLE();
+ __GPIOB_CLK_ENABLE();
+ __GPIOC_CLK_ENABLE();
+ __GPIOH_CLK_ENABLE();
+
+ GPIO_InitTypeDef GPIO_InitStruct;
+
+ // UART1_TX, UART1_RTS & UART1_CTS to analog nopull - RX could be a wakeup source
+ GPIO_InitStruct.Pin = GPIO_PIN_9 | GPIO_PIN_11 | GPIO_PIN_12;
+ GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
+ GPIO_InitStruct.Pull = GPIO_NOPULL;
+ HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
+
+ // I2C_SDA & I2C_SCL to analog nopull
+ GPIO_InitStruct.Pin = GPIO_PIN_8 | GPIO_PIN_9;
+ GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
+ GPIO_InitStruct.Pull = GPIO_NOPULL;
+ HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
+
+ // SPI_MOSI, SPI_MISO, SPI_SCK, & SPI_NSS to analog nopull
+ GPIO_InitStruct.Pin = GPIO_PIN_12 | GPIO_PIN_13 | GPIO_PIN_14 | GPIO_PIN_15;
+ GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
+ GPIO_InitStruct.Pull = GPIO_NOPULL;
+ HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
+
+ // iterate through potential wake pins - leave the configured wake pin alone if one is needed
+ if (_dot->getWakePin() != WAKE || _dot->getWakeMode() == mDot::RTC_ALARM) {
+ GPIO_InitStruct.Pin = GPIO_PIN_0;
+ GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
+ GPIO_InitStruct.Pull = GPIO_NOPULL;
+ HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
+ }
+ if (_dot->getWakePin() != GPIO0 || _dot->getWakeMode() == mDot::RTC_ALARM) {
+ GPIO_InitStruct.Pin = GPIO_PIN_4;
+ GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
+ GPIO_InitStruct.Pull = GPIO_NOPULL;
+ HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
+ }
+ if (_dot->getWakePin() != GPIO1 || _dot->getWakeMode() == mDot::RTC_ALARM) {
+ GPIO_InitStruct.Pin = GPIO_PIN_5;
+ GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
+ GPIO_InitStruct.Pull = GPIO_NOPULL;
+ HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
+ }
+ if (_dot->getWakePin() != GPIO2 || _dot->getWakeMode() == mDot::RTC_ALARM) {
+ GPIO_InitStruct.Pin = GPIO_PIN_0;
+ GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
+ GPIO_InitStruct.Pull = GPIO_NOPULL;
+ HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
+ }
+ if (_dot->getWakePin() != GPIO3 || _dot->getWakeMode() == mDot::RTC_ALARM) {
+ GPIO_InitStruct.Pin = GPIO_PIN_2;
+ GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
+ GPIO_InitStruct.Pull = GPIO_NOPULL;
+ HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
+ }
+ if (_dot->getWakePin() != UART1_RX || _dot->getWakeMode() == mDot::RTC_ALARM) {
+ GPIO_InitStruct.Pin = GPIO_PIN_10;
+ GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
+ GPIO_InitStruct.Pull = GPIO_NOPULL;
+ HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
+ }
+#endif
+
+ _dot->sleep(_dot->getWakeInterval(), _dot->getWakeMode(), standby);
+
+#if defined(TARGET_XDOT_L151CC)
+ xdot_restore_gpio_state();
+#endif
+}
+
+std::string CommandTerminal::formatPacketData(const std::vector<uint8_t>& data, const uint8_t& format) {
+ if (format == mDot::HEXADECIMAL)
+ return mts::Text::bin2hexString(data);
+ else
+ return std::string(data.begin(), data.end());
+}
+
+bool CommandTerminal::waitForEscape(int timeout, mDot* dot, WaitType wait) {
+ Timer timer;
+
+ timer.start();
+ while (timer.read_ms() < timeout) {
+
+ if (dot != NULL) {
+ if (wait == WAIT_SEND && (!dot->getIsTransmitting())) {
+ return false;
+ }
+ }
+
+ if (_serialp != NULL && _serialp->escaped()) {
+ _serialp->clearEscaped();
+ return true;
+ }
+
+ osDelay(10);
+ }
+
+ return false;
+}
+
+void CommandTerminal::wakeup(void) {
+}
+
+void CommandTerminal::RadioEvent::PacketRx(uint8_t port, uint8_t *payload, uint16_t size, int16_t rssi, int8_t snr, lora::DownlinkControl ctrl, uint8_t slot, uint8_t retries) {
+ mDotEvent::PacketRx(port, payload, size, rssi, snr, ctrl, retries);
+
+ if (serial_data_mode) {
+ logDebug("Rx %d bytes", size);
+ if (size > 0) {
+ CommandTerminal::Serial()->write((char*) RxPayload, RxPayloadSize);
+ }
+ if (!CommandTerminal::Serial()->readable() && _dot->getAckRequested() && _dot->getClass() == "C") {
+ _sendAck = true;
+ }
+ }
+}
+
+CommandTerminal::~CommandTerminal() {
+ delete _events;
+}
