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
CommandTerminal/CommandTerminal.cpp
- Committer:
- Mike Fiore
- Date:
- 2015-06-25
- Revision:
- 1:e52ae6584f1c
- Child:
- 2:e5eebd74d36d
File content as of revision 1:e52ae6584f1c:
#include "ctype.h"
#include "CommandTerminal.h"
#include "Command.h"
#include <cstdarg>
extern "C" {
#include "wakeup.h"
extern void pin_function(PinName pin, int data);
}
const char CommandTerminal::banner[] = "\r\n\nMultiTech Systems LoRa XBee Module\r\n\n";
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::done[] = "\r\nOK\r\n";
const char CommandTerminal::error[] = "\r\nERROR\r\n";
// Escape sequence...
const char CommandTerminal::escape_sequence[] = "+++";
mts::MTSSerial* CommandTerminal::_serialp = NULL;
void CommandTerminal::addCommand(Command* cmd) {
_commands.push_back(cmd);
}
CommandTerminal::CommandTerminal(mts::MTSSerial& serial, mDot* dot)
:
_serial(serial),
_dot(dot),
_mode(mDot::COMMAND_MODE),
_sleep_standby(false),
_xbee_on_sleep(XBEE_ON_SLEEP),
_serial_up(false) {
_serialp = &serial;
wakeup_init(_dot->getSerialWakeInterval());
addCommand(new CmdAttention(_dot));
addCommand(new CmdIdentification(_dot, serial));
addCommand(new CmdResetCpu(_dot, serial));
addCommand(new CmdDummy(_dot, "Enable/Disable Echo", "ATE0/1", "ATE0: disable, ATE1: enable"));
addCommand(new CmdDummy(_dot, "Enable/Disable Verbose", "ATV0/1", "ATV0: disable, ATV1: enable"));
addCommand(new CmdFactoryDefault(_dot));
addCommand(new CmdSaveConfig(_dot));
addCommand(new CmdDisplayConfig(_dot, serial));
addCommand(new CmdDisplayStats(_dot, serial));
addCommand(new CmdSerialBaudRate(_dot, serial));
addCommand(new CmdDebugBaudRate(_dot, serial));
addCommand(new CmdStartUpMode(_dot, serial));
addCommand(new CmdFrequencyBand(_dot, serial));
addCommand(new CmdFrequencySubBand(_dot, serial));
addCommand(new CmdPublicNetwork(_dot, serial));
addCommand(new CmdDeviceId(_dot, serial));
addCommand(new CmdNetworkAddress(_dot, serial));
addCommand(new CmdNetworkSessionKey(_dot, serial));
addCommand(new CmdDataSessionKey(_dot, serial));
addCommand(new CmdNetworkKey(_dot, serial));
addCommand(new CmdNetworkId(_dot, serial));
addCommand(new CmdJoinRequest(_dot, serial));
addCommand(new CmdJoinRetries(_dot, serial));
addCommand(new CmdNetworkJoinMode(_dot, serial));
addCommand(new CmdNetworkJoinStatus(_dot, serial));
addCommand(new CmdNetworkLinkCheck(_dot, serial));
addCommand(new CmdLinkCheckCount(_dot, serial));
addCommand(new CmdLinkCheckThreshold(_dot, serial));
addCommand(new CmdEncryption(_dot, serial));
addCommand(new CmdRssi(_dot, serial));
addCommand(new CmdSnr(_dot, serial));
addCommand(new CmdDataPending(_dot, serial));
addCommand(new CmdTxDataRate(_dot, serial));
addCommand(new CmdTxPower(_dot, serial));
addCommand(new CmdTxFrequency(_dot, serial));
addCommand(new CmdTxInverted(_dot, serial));
addCommand(new CmdTxWait(_dot, serial));
addCommand(new CmdTxChannel(_dot, serial));
addCommand(new CmdTxNextMs(_dot, serial));
addCommand(new CmdTimeOnAir(_dot, serial));
addCommand(new CmdRxDataRate(_dot, serial));
addCommand(new CmdRxFrequency(_dot, serial));
addCommand(new CmdRxOutput(_dot, serial));
addCommand(new CmdRxInverted(_dot, serial));
addCommand(new CmdErrorCorrection(_dot, serial));
addCommand(new CmdCRC(_dot, serial));
addCommand(new CmdAdaptiveDataRate(_dot, serial));
addCommand(new CmdACKAttempts(_dot, serial));
addCommand(new CmdSendString(_dot, serial));
addCommand(new CmdSendStringHighBW(_dot, serial));
addCommand(new CmdSendBinary(_dot, serial));
addCommand(new CmdSendStringOnInterval(_dot, serial));
addCommand(new CmdReceiveOnce(_dot, serial));
addCommand(new CmdReceiveContinuous(_dot, serial));
addCommand(new CmdDummy(_dot, "Serial Data Mode", "AT+SD", "Reads serial data and sends Lora packets (escape sequence: +++)"));
addCommand(new CmdSerialWakeInterval(_dot, serial));
addCommand(new CmdSerialWakeDelay(_dot, serial));
addCommand(new CmdSerialReceiveTimeout(_dot, serial));
addCommand(new CmdPing(_dot, serial));
addCommand(new CmdLogLevel(_dot, serial));
}
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 (std::vector<Command*>::iterator it = _commands.begin(); it != _commands.end(); ++it) {
name = (*it)->name();
text = (*it)->text();
desc = (*it)->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 _serial.writeable();
}
bool CommandTerminal::readable() {
return _serial.readable();
}
char CommandTerminal::read() {
char ch;
_serial.read(&ch, 1);
return ch;
}
void CommandTerminal::write(const char* message) {
while (!writeable())
;
_serial.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())
;
_serial.write(buff, size);
va_end(ap);
}
void CommandTerminal::serial_loop() {
Timer serial_read_timer;
std::vector<uint8_t> serial_buffer;
std::string escape_buffer;
Timer escape_timer;
int escape_delay = 100;
uint8_t max_send_size;
_serial_up = true;
_xbee_on_sleep = GPIO_PIN_SET;
if (_dot->getFrequencyBand() == mDot::FB_915)
max_send_size = mDot::MaxLengths_915[_dot->getTxDataRate()];
else
max_send_size = mDot::MaxLengths_868[_dot->getTxDataRate()];
DEBUG_PRINTF("Awake\r\n");
wakeup(_sleep_standby);
char ch;
if (readable()) {
ch = read();
serial_buffer.push_back(ch);
if (escape_timer.read_ms() > escape_delay && ch == '+') {
escape_buffer += ch;
escape_timer.reset();
} else {
_serial_up = true;
escape_buffer.clear();
}
if (escape_buffer.length() == 3 && escape_buffer.find(escape_sequence) == 0) {
_mode = mDot::COMMAND_MODE;
DEBUG_PRINTF("Exit serial mode\r\n");
escape_timer.stop();
escape_buffer.clear();
write(done);
return;
}
}
if (_serial_up) {
osDelay(_dot->getSerialWakeDelay());
serial_read_timer.start();
while (_serial_up && serial_read_timer.read_ms() < _dot->getSerialReceiveTimeout()) {
while (readable() && serial_buffer.size() < max_send_size) {
serial_buffer.push_back(read());
serial_read_timer.reset();
}
}
serial_read_timer.stop(), serial_read_timer.reset();
if (!serial_buffer.empty()) {
_serial_up = false;
_xbee_on_sleep = GPIO_PIN_RESET;
if (!_dot->getIsTransmitting()) {
std::vector<uint8_t> recv_buffer;
DEBUG_PRINTF("Received serial data, sending out radio.\r\n");
if (!_dot->send(serial_buffer))
DEBUG_PRINTF("Send failed.\r\n");
if (_dot->recv(recv_buffer))
_serial.writef("%s\r\n", formatPacketData(recv_buffer, _dot->getRxOutput()).c_str());
} else {
DEBUG_PRINTF("Radio is busy, cannot send.\r\n");
}
serial_buffer.clear();
} else {
DEBUG_PRINTF("No data received from serial to send.\r\n");
}
_serial_up = false;
}
sleep(_sleep_standby);
}
bool CommandTerminal::autoJoinCheck() {
std::string escape_buffer;
char ch;
int sleep = 1000;
int escape_timeout = 1000;
Timer tmr;
Timer escape_tmr;
int cnt = 0;
while (!_dot->getNetworkJoinStatus()) {
wakeup(_sleep_standby);
write("\r\nJoining network... ");
// wait one second for possible escape
tmr.reset();
tmr.start();
escape_tmr.reset();
escape_tmr.start();
while (tmr.read_ms() < 1000) {
if (_serial.readable()) {
_serial.read(&ch, 1);
escape_buffer += ch;
}
if (escape_buffer.find(escape_sequence) != std::string::npos) {
_mode = mDot::COMMAND_MODE;
write("Join Canceled\r\n");
write(done);
return true;
}
if (escape_tmr.read_ms() > escape_timeout)
escape_buffer.clear();
}
if (_dot->joinNetworkOnce() == mDot::MDOT_OK) {
write("Network Joined\r\n");
write(done);
return false;
}
write("Network Join failed\r\n");
write(error);
if (cnt++ > _dot->getJoinRetries()) {
cnt = 0;
if (_dot->getFrequencyBand() == mDot::FB_915) {
uint8_t band = ((_dot->getFrequencySubBand()) % 8) + 1;
DEBUG_PRINTF("Join retries exhausted, switching to sub band %u\r\n", band);
_dot->setFrequencySubBand(band);
}
if (sleep < 60 * 60 * 1000)
sleep *= 2;
}
tmr.reset();
tmr.start();
escape_tmr.reset();
escape_tmr.start();
while (tmr.read_ms() < sleep) {
if (_serial.readable()) {
_serial.read(&ch, 1);
escape_buffer += ch;
}
if (escape_buffer.find(escape_sequence) != std::string::npos) {
_mode = mDot::COMMAND_MODE;
return true;
}
if (escape_tmr.read_ms() > escape_timeout)
escape_buffer.clear();
}
}
return false;
}
void CommandTerminal::start() {
_dot->resetNetworkSession();
char ch;
bool running = true;
bool echo = _dot->getEcho();
std::string command;
std::vector<std::string> args;
if (_dot->getStartUpMode() == mDot::SERIAL_MODE) {
command = "AT+SD\n";
std::string escape_buffer;
char ch;
int escape_timeout = 1000;
Timer tmr;
Timer escape_tmr;
// wait one second for possible escape
tmr.reset();
tmr.start();
escape_tmr.reset();
escape_tmr.start();
while (tmr.read_ms() < escape_timeout) {
if (_serial.readable()) {
_serial.read(&ch, 1);
escape_buffer += ch;
}
if (escape_buffer.find(escape_sequence) != std::string::npos) {
_mode = mDot::COMMAND_MODE;
command.clear();
break;
}
if (escape_tmr.read_ms() > escape_timeout)
escape_buffer.clear();
osDelay(1);
}
}
bool join_canceled = false;
//Run terminal session
while (running) {
if (!join_canceled && _dot->getJoinMode() == mDot::AUTO_OTA) {
join_canceled = autoJoinCheck();
if (join_canceled)
command.clear();
}
if (_dot->getJoinMode() != mDot::AUTO_OTA || (!join_canceled && _dot->getJoinMode() == mDot::AUTO_OTA)) {
switch (_mode) {
case mDot::SERIAL_MODE:
// signal wakeup, read serial and output to radio
serial_loop();
continue;
break;
default:
break;
}
}
ch = '\0';
// read characters
if (readable()) {
ch = read();
if (ch == '\b') {
if (!command.empty()) {
writef("\b \b");
command.erase(command.size() - 1);
}
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("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("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] == "AT+SD") {
DEBUG_PRINTF("Enter Serial Mode\r\n");
_mode = mDot::SERIAL_MODE;
} 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 (std::vector<Command*>::iterator it = _commands.begin(); it != _commands.end() && !found; ++it) {
Command* cmd = *it;
// match CMD or CMD? syntax if command is queryable
if (lookfor == cmd->text() && (!query || (query && cmd->queryable()))) {
found = true;
if (args[0] == "HELP") {
writef("%s%s", cmd->help(), newline);
write(done);
}
else if (args.size() > 1 && args[1] == "?") {
writef("%s%s", cmd->usage().c_str(), newline);
write(done);
} else if (!cmd->verify(args)) {
writef("%s%s", cmd->errorMessage().c_str(), newline);
writef("%s", error);
} else {
if (cmd->action(args) == 0) {
writef("%s", done);
} else {
writef("%s%s", cmd->errorMessage().c_str(), newline);
writef("%s", error);
}
}
}
}
if (!found) {
writef("%s", command_error);
writef("%s", error);
}
}
command.clear();
}
}
std::string CommandTerminal::formatPacketData(const std::vector<uint8_t>& data, const uint8_t& format) {
if (format == mDot::HEXADECIMAL) {
return mts::Text::bin2hexString(data);
} else {
std::string data_str;
for (int i = 0; i < data.size(); i++)
data_str.append((const char*)data[i], 1);
return data_str;
}
}
void CommandTerminal::sleep(bool standby) {
_serial_up = false;
_xbee_on_sleep = GPIO_PIN_RESET;
HAL_PWREx_EnableMainRegulatorLowVoltage();
HAL_PWREx_EnableFlashPowerDown();
HAL_PWREx_EnableLowRegulatorLowVoltage();
DEBUG_PRINTF("Sleep\r\n");
_dot->sleep();
if (standby) {
DEBUG_PRINTF("RECORD UPLINK: %lu\r\n", _dot->getUpLinkCounter());
RTC_WriteBackupRegister(RTC_BKP_DR0, _dot->getUpLinkCounter());
HAL_PWR_EnableBkUpAccess();
HAL_EnableDBGStandbyMode();
HAL_PWR_DisableWakeUpPin (PWR_WAKEUP_PIN1);
__HAL_PWR_CLEAR_FLAG(PWR_FLAG_WU); // InterruptIn wakeon_serial(XBEE_DIN);
// Application is reloaded after wake-up from standby
HAL_PWR_EnterSTANDBYMode ();
} else {
pin_function(PA_0, STM_PIN_DATA(STM_MODE_ANALOG, GPIO_NOPULL, 0));
pin_function(PA_1, STM_PIN_DATA(STM_MODE_ANALOG, GPIO_NOPULL, 0));
// pin_function(PA_2, STM_PIN_DATA(STM_MODE_ANALOG, GPIO_NOPULL, 0));
// pin_function(PA_3, STM_PIN_DATA(STM_MODE_ANALOG, GPIO_NOPULL, 0));
pin_function(PA_4, STM_PIN_DATA(STM_MODE_ANALOG, GPIO_NOPULL, 0));
pin_function(PA_5, STM_PIN_DATA(STM_MODE_ANALOG, GPIO_NOPULL, 0));
pin_function(PA_6, STM_PIN_DATA(STM_MODE_ANALOG, GPIO_NOPULL, 0));
pin_function(PA_7, STM_PIN_DATA(STM_MODE_ANALOG, GPIO_NOPULL, 0));
// pin_function(PA_8, STM_PIN_DATA(STM_MODE_ANALOG, GPIO_NOPULL, 0));
// pin_function(PA_9, STM_PIN_DATA(STM_MODE_ANALOG, GPIO_NOPULL, 0));
// pin_function(PA_10, STM_PIN_DATA(STM_MODE_ANALOG, GPIO_NOPULL, 0));
pin_function(PA_11, STM_PIN_DATA(STM_MODE_ANALOG, GPIO_NOPULL, 0));
pin_function(PA_12, STM_PIN_DATA(STM_MODE_ANALOG, GPIO_NOPULL, 0));
pin_function(PA_15, STM_PIN_DATA(STM_MODE_ANALOG, GPIO_NOPULL, 0));
pin_function(PB_0, STM_PIN_DATA(STM_MODE_ANALOG, GPIO_NOPULL, 0));
pin_function(PB_1, STM_PIN_DATA(STM_MODE_ANALOG, GPIO_NOPULL, 0));
pin_function(PB_2, STM_PIN_DATA(STM_MODE_ANALOG, GPIO_NOPULL, 0));
pin_function(PC_1, STM_PIN_DATA(STM_MODE_ANALOG, GPIO_NOPULL, 0));
pin_function(PC_4, STM_PIN_DATA(STM_MODE_ANALOG, GPIO_NOPULL, 0));
pin_function(PC_5, STM_PIN_DATA(STM_MODE_ANALOG, GPIO_NOPULL, 0));
pin_function(PC_9, STM_PIN_DATA(STM_MODE_ANALOG, GPIO_NOPULL, 0));
// pin_function(PC_13, STM_PIN_DATA(STM_MODE_ANALOG, GPIO_NOPULL, 0));
pin_function(PD_2, STM_PIN_DATA(STM_MODE_ANALOG, GPIO_NOPULL, 0));
HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI);
}
wakeup_clear();
EXTI->PR = (1 << 22);
// After wake-up from STOP reconfigure the PLL
SetSysClock();
}
bool CommandTerminal::waitForEscape(int timeout, mDot* dot, WaitType wait) {
Timer timer;
Timer escape_timer;
std::string escape_buffer;
int escape_timeout = 1000;
timer.start();
while (timer.read_ms() < timeout) {
if (dot != NULL) {
if (wait == WAIT_SEND && (!dot->getIsTransmitting())) {
return false;
}
}
if (_serialp != NULL && _serialp->readable()) {
if (escape_buffer == "")
escape_timer.start();
char ch;
_serialp->read(&ch, 1);
escape_buffer += ch;
if (escape_buffer == CommandTerminal::escape_sequence)
return true;
}
if (escape_timer.read_ms() > escape_timeout) {
escape_buffer = "";
escape_timer.stop(), escape_timer.reset();
}
osDelay(10);
}
return false;
}
void CommandTerminal::wakeup(bool standby) {
HAL_PWREx_DisableMainRegulatorLowVoltage();
HAL_PWREx_DisableFlashPowerDown();
HAL_PWREx_DisableLowRegulatorLowVoltage();
if (standby) {
HAL_DisableDBGStandbyMode();
HAL_PWR_EnableWakeUpPin (PWR_WAKEUP_PIN1);
}
_dot->wakeup();
}
