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: MTS-Serial libxDot-mbed5
CommandTerminal/CommandTerminal.cpp
- Committer:
- jenkins@jenkinsdm1
- Date:
- 2017-06-19
- Revision:
- 16:d5cf2af81a6d
- Parent:
- 14:f9a77400b622
- Child:
- 18:72b6b49d363d
File content as of revision 16:d5cf2af81a6d:
#include "ctype.h"
#include "CommandTerminal.h"
#include "Command.h"
#include "MTSLog.h"
#include "ChannelPlan.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(),
CmdDefaultFrequencyBand(),
CmdFrequencyBand(),
CmdFrequencySubBand(),
CmdPublicNetwork(),
CmdDeviceId(),
CmdDeviceClass(),
CmdLbt(),
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(),
CmdLBTRSSI(),
#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,
CmdDefaultFrequencyBand::verify,
CmdFrequencyBand::verify,
CmdFrequencySubBand::verify,
CmdPublicNetwork::verify,
CmdDeviceId::verify,
CmdDeviceClass::verify,
CmdLbt::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,
CmdLBTRSSI::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,
CmdDefaultFrequencyBand::action,
CmdFrequencyBand::action,
CmdFrequencySubBand::action,
CmdPublicNetwork::action,
CmdDeviceId::action,
CmdDeviceClass::action,
CmdLbt::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,
CmdLBTRSSI::action,
#endif
};
CommandTerminal::CommandTerminal(mts::ATSerial& serial) :
_serial(serial),
_mode(mDot::COMMAND_MODE),
_sleep_standby(true),
#if defined(TARGET_MTS_MDOT_F411RE)
_xbee_on_sleep(XBEE_ON_SLEEP),
#else
_xbee_on_sleep(GPIO2),
#endif
_autoOTAEnabled(false)
{
_serialp = &serial;
}
void CommandTerminal::init() {
_dot->setEvents(_events);
}
void CommandTerminal::printHelp() {
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 (lora::ChannelPlan::IsPlanFixed(_dot->getFrequencyBand())) {
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;
#if defined(TARGET_MTS_MDOT_F411RE)
std::deque<std::string> history;
int history_index = -1;
#endif
std::vector<std::string> args;
bool join_canceled = false;
_autoOTAEnabled = _dot->getJoinMode() == mDot::AUTO_OTA;
if (_dot->getStartUpMode() == mDot::SERIAL_MODE) {
serial_data_mode = true;
_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 defined(TARGET_MTS_MDOT_F411RE)
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());
}
}
#endif
}
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");
#if defined(TARGET_MTS_MDOT_F411RE)
//Read the board ID. If all 0's, it is revision B. This hardware does not support deep sleep.
DigitalIn ID2(PC_4);
DigitalIn ID1(PC_5);
DigitalIn ID0(PD_2);
if(ID2 == 0 && ID1 == 0 && ID0 == 0 && _sleep_standby == 1){
_sleep_standby = 0;
logWarning("This hardware version does not support deep sleep. Using sleep mode instead.");
}
#endif
write(done);
osDelay(5);
this->sleep(_sleep_standby);
osDelay(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 defined(TARGET_MTS_MDOT_F411RE)
if (history.size() == 0 || history.front() != command)
history.push_front(command);
history_index = -1;
command.clear();
while (history.size() > 10)
history.pop_back();
#else
command.clear();
#endif
}
}
void CommandTerminal::sleep(bool standby) {
_xbee_on_sleep = GPIO_PIN_RESET;
_serial.rxClear();
_serial.txClear();
#if defined(TARGET_XDOT_L151CC)
xdot_save_gpio_state();
/* GPIO Ports Clock Enable */
__GPIOA_CLK_ENABLE();
__GPIOB_CLK_ENABLE();
__GPIOC_CLK_ENABLE();
__GPIOH_CLK_ENABLE();
GPIO_InitTypeDef GPIO_InitStruct;
// UART1_TX, UART1_RTS & UART1_CTS to analog nopull - RX could be a wakeup source
GPIO_InitStruct.Pin = GPIO_PIN_9 | GPIO_PIN_11 | GPIO_PIN_12;
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
// I2C_SDA & I2C_SCL to analog nopull
GPIO_InitStruct.Pin = GPIO_PIN_8 | GPIO_PIN_9;
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
// SPI_MOSI, SPI_MISO, SPI_SCK, & SPI_NSS to analog nopull
GPIO_InitStruct.Pin = GPIO_PIN_12 | GPIO_PIN_13 | GPIO_PIN_14 | GPIO_PIN_15;
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
// iterate through potential wake pins - leave the configured wake pin alone if one is needed
if (_dot->getWakePin() != WAKE || _dot->getWakeMode() == mDot::RTC_ALARM) {
GPIO_InitStruct.Pin = GPIO_PIN_0;
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}
if (_dot->getWakePin() != GPIO0 || _dot->getWakeMode() == mDot::RTC_ALARM) {
GPIO_InitStruct.Pin = GPIO_PIN_4;
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}
if (_dot->getWakePin() != GPIO1 || _dot->getWakeMode() == mDot::RTC_ALARM) {
GPIO_InitStruct.Pin = GPIO_PIN_5;
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}
if (_dot->getWakePin() != GPIO2 || _dot->getWakeMode() == mDot::RTC_ALARM) {
GPIO_InitStruct.Pin = GPIO_PIN_0;
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
}
if (_dot->getWakePin() != GPIO3 || _dot->getWakeMode() == mDot::RTC_ALARM) {
GPIO_InitStruct.Pin = GPIO_PIN_2;
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
}
if (_dot->getWakePin() != UART1_RX || _dot->getWakeMode() == mDot::RTC_ALARM) {
GPIO_InitStruct.Pin = GPIO_PIN_10;
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}
#else
uint32_t portA[6];
uint32_t portB[6];
uint32_t portC[6];
uint32_t portD[6];
uint32_t portH[6];
//Save the GPIO state.
portA[0] = GPIOA->MODER;
portA[1] = GPIOA->OTYPER;
portA[2] = GPIOA->OSPEEDR;
portA[3] = GPIOA->PUPDR;
portA[4] = GPIOA->AFR[0];
portA[5] = GPIOA->AFR[1];
portB[0] = GPIOB->MODER;
portB[1] = GPIOB->OTYPER;
portB[2] = GPIOB->OSPEEDR;
portB[3] = GPIOB->PUPDR;
portB[4] = GPIOB->AFR[0];
portB[5] = GPIOB->AFR[1];
portC[0] = GPIOC->MODER;
portC[1] = GPIOC->OTYPER;
portC[2] = GPIOC->OSPEEDR;
portC[3] = GPIOC->PUPDR;
portC[4] = GPIOC->AFR[0];
portC[5] = GPIOC->AFR[1];
portD[0] = GPIOD->MODER;
portD[1] = GPIOD->OTYPER;
portD[2] = GPIOD->OSPEEDR;
portD[3] = GPIOD->PUPDR;
portD[4] = GPIOD->AFR[0];
portD[5] = GPIOD->AFR[1];
portH[0] = GPIOH->MODER;
portH[1] = GPIOH->OTYPER;
portH[2] = GPIOH->OSPEEDR;
portH[3] = GPIOH->PUPDR;
portH[4] = GPIOH->AFR[0];
portH[5] = GPIOH->AFR[1];
/* GPIO Ports Clock Enable */
__GPIOA_CLK_ENABLE();
__GPIOB_CLK_ENABLE();
__GPIOC_CLK_ENABLE();
GPIO_InitTypeDef GPIO_InitStruct;
// Set port A pins to analog nopull
GPIO_InitStruct.Pin = GPIO_PIN_2 | GPIO_PIN_6 | GPIO_PIN_8 | GPIO_PIN_9 | GPIO_PIN_10
| GPIO_PIN_12 | GPIO_PIN_13 | GPIO_PIN_14 | GPIO_PIN_15;
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
// Set port B pins to analog nopull
GPIO_InitStruct.Pin = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_3 | GPIO_PIN_4;
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
// Set port C pins to analog nopull
GPIO_InitStruct.Pin = GPIO_PIN_9 | GPIO_PIN_13;
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
// iterate through potential wake pins - leave the configured wake pin alone if one is needed
// XBEE_DIN - PA3
// XBEE_DIO2 - PA5
// XBEE_DIO3 - PA4
// XBEE_DIO4 - PA7
// XBEE_DIO5 - PC1
// XBEE_DIO6 - PA1
// XBEE_DIO7 - PA0
// XBEE_SLEEPRQ - PA11
if (_dot->getWakePin() != XBEE_DIN || _dot->getWakeMode() == mDot::RTC_ALARM) {
GPIO_InitStruct.Pin = GPIO_PIN_3;
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}
if (_dot->getWakePin() != XBEE_DIO2 || _dot->getWakeMode() == mDot::RTC_ALARM) {
GPIO_InitStruct.Pin = GPIO_PIN_5;
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}
if (_dot->getWakePin() != XBEE_DIO3 || _dot->getWakeMode() == mDot::RTC_ALARM) {
GPIO_InitStruct.Pin = GPIO_PIN_4;
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}
if (_dot->getWakePin() != XBEE_DIO4 || _dot->getWakeMode() == mDot::RTC_ALARM) {
GPIO_InitStruct.Pin = GPIO_PIN_7;
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}
if (_dot->getWakePin() != XBEE_DIO5 || _dot->getWakeMode() == mDot::RTC_ALARM) {
GPIO_InitStruct.Pin = GPIO_PIN_1;
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
}
if (_dot->getWakePin() != XBEE_DIO6 || _dot->getWakeMode() == mDot::RTC_ALARM) {
GPIO_InitStruct.Pin = GPIO_PIN_1;
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}
if (_dot->getWakePin() != XBEE_DIO7 || _dot->getWakeMode() == mDot::RTC_ALARM) {
GPIO_InitStruct.Pin = GPIO_PIN_0;
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}
if (_dot->getWakePin() != XBEE_SLEEPRQ|| _dot->getWakeMode() == mDot::RTC_ALARM) {
GPIO_InitStruct.Pin = GPIO_PIN_11;
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}
#endif
_dot->sleep(_dot->getWakeInterval(), _dot->getWakeMode(), standby);
#if defined(TARGET_XDOT_L151CC)
xdot_restore_gpio_state();
#else
//Restore the GPIO state.
GPIOA->MODER = portA[0];
GPIOA->OTYPER = portA[1];
GPIOA->OSPEEDR = portA[2];
GPIOA->PUPDR = portA[3];
GPIOA->AFR[0] = portA[4];
GPIOA->AFR[1] = portA[5];
GPIOB->MODER = portB[0];
GPIOB->OTYPER = portB[1];
GPIOB->OSPEEDR = portB[2];
GPIOB->PUPDR = portB[3];
GPIOB->AFR[0] = portB[4];
GPIOB->AFR[1] = portB[5];
GPIOC->MODER = portC[0];
GPIOC->OTYPER = portC[1];
GPIOC->OSPEEDR = portC[2];
GPIOC->PUPDR = portC[3];
GPIOC->AFR[0] = portC[4];
GPIOC->AFR[1] = portC[5];
GPIOD->MODER = portD[0];
GPIOD->OTYPER = portD[1];
GPIOD->OSPEEDR = portD[2];
GPIOD->PUPDR = portD[3];
GPIOD->AFR[0] = portD[4];
GPIOD->AFR[1] = portD[5];
GPIOH->MODER = portH[0];
GPIOH->OTYPER = portH[1];
GPIOH->OSPEEDR = portH[2];
GPIOH->PUPDR = portH[3];
GPIOH->AFR[0] = portH[4];
GPIOH->AFR[1] = portH[5];
#endif
_serial.rxClear();
_serial.txClear();
}
std::string CommandTerminal::formatPacketData(const std::vector<uint8_t>& data, const uint8_t& format) {
if (format == mDot::HEXADECIMAL)
return mts::Text::bin2hexString(data);
else
return std::string(data.begin(), data.end());
}
bool CommandTerminal::waitForEscape(int timeout, mDot* dot, WaitType wait) {
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;
}