mDot AT-Firmware for testing
Dependencies: MTS-Serial libmDot-mbed5
Fork of Dot-AT-Firmware by
Diff: CommandTerminal/CommandTerminal.cpp
- Revision:
- 1:e52ae6584f1c
- Child:
- 2:e5eebd74d36d
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/CommandTerminal/CommandTerminal.cpp Thu Jun 25 10:23:41 2015 -0500 @@ -0,0 +1,684 @@ +#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(); + +}