#pragma once #include #include #include #include #include "light_sensors.hpp" namespace detail { constexpr auto ENDL = "\r\n"; constexpr auto HELP_CMD = "help"; constexpr auto READ_CMD = "read"; constexpr auto READ_CSV_CMD = "read csv"; constexpr auto RANGE_CMD = "range"; constexpr auto VERSION_CMD = "version"; constexpr auto VERSION = "1.2"; static inline bool substringEquals(const char* str1, const char* str2, const size_t& size) { return (std::strncmp(str1, str2, size) == 0); } static inline bool stringEquals(const char* str1, const char* str2, const size_t& size) { if(size == std::strlen(str2)) { return substringEquals(str1, str2, size); } return false; } } // namespace detail template class Terminal { public: static void init() { m_serial.init(); m_serial << detail::ENDL; printVersion(); m_serial << detail::ENDL << "$ "; } static void callback() { if(receiveInput()) { parseInput(); } m_serial.flushTx(); } private: static constexpr auto INPUT_BUFFER_SIZE = 128; static constexpr auto BACKSPACE = uint8_t{0x7f}; static constexpr auto CTRL_C = uint8_t{0x03}; static Uart m_serial; static char m_inputBuffer[INPUT_BUFFER_SIZE]; static uint16_t m_inputSize; static LightSensors m_lightSensors; static bool receiveInput() { uint8_t inputByte; while(m_serial.rxByte(inputByte)) { if(std::isprint(inputByte) || inputByte == CTRL_C) { m_inputBuffer[m_inputSize++] = inputByte; // Handle Ctrl + C if(inputByte == CTRL_C) { m_serial << "^C" << detail::ENDL; return true; } // Echo else { m_serial << static_cast(inputByte); } } // Handle backspace if(inputByte == BACKSPACE && m_inputSize > 0) { m_serial << "\b \b"; --m_inputSize; } // Handle line terminator else if(inputByte == '\r' || inputByte == '\n') { // Consume possible second line terminator if(m_serial.peek(inputByte) && (inputByte == '\r' || inputByte == '\n')) { m_serial.rxByte(inputByte); } m_serial << detail::ENDL; return true; } if(m_inputSize >= INPUT_BUFFER_SIZE) { m_serial << detail::ENDL << "WARNING: Terminal input buffer overflow!" << detail::ENDL; return true; } } return false; } static void parseInput() { if(m_inputSize) { if(m_inputBuffer[m_inputSize - 1] == CTRL_C) { handleCtrlC(); } else { if(detail::substringEquals(m_inputBuffer, detail::HELP_CMD, m_inputSize)) { printHelp(); } else if(detail::substringEquals(m_inputBuffer, detail::READ_CMD, m_inputSize)) { readSensors(); } else if(detail::stringEquals(m_inputBuffer, detail::READ_CSV_CMD, m_inputSize)) { readSensorsCsv(); } else if(detail::stringEquals(m_inputBuffer, detail::RANGE_CMD, m_inputSize)) { getSensorsRange(); } else if(detail::substringEquals(m_inputBuffer, detail::VERSION_CMD, m_inputSize)) { printVersion(); } else { printUnknown(); } } } m_inputSize = 0; m_serial << "$ "; } static void handleCtrlC() { m_serial << "Abort!" << detail::ENDL; } static void printHelp() { m_serial << "AdaptiveBrightness command overview: " << detail::ENDL; m_serial << detail::HELP_CMD << " .......: prints this help message" << detail::ENDL; m_serial << detail::READ_CMD << " .......: reads and displays all LDR values" << detail::ENDL; m_serial << detail::READ_CSV_CMD << " ...: read LDR values as csv" << detail::ENDL; m_serial << detail::RANGE_CMD << " ......: print LDR sensor range" << detail::ENDL; m_serial << detail::VERSION_CMD << " ....: displays firmware version" << detail::ENDL; } static void readSensors() { const auto sensorValues = m_lightSensors.getValues(); m_serial << "Sensor values: " << detail::ENDL; for(size_t i = 0; i < sensorValues.size(); ++i) { m_serial << "LDR"; m_serial.txNumber(i + 1); m_serial << ": "; m_serial.template txNumber(sensorValues[i]); m_serial << " - "; m_serial.template txNumber(sensorValues[i] * 100 / m_lightSensors.MAX_VALUE); m_serial << "%" << detail::ENDL; } } static void readSensorsCsv() { const auto sensorValues = m_lightSensors.getValues(); for(size_t i = 0; i < sensorValues.size(); ++i) { m_serial.txNumber(sensorValues[i]); m_serial << ((i + 1 == sensorValues.size()) ? "" : ","); } m_serial << detail::ENDL; } static void getSensorsRange() { m_serial << "0,"; m_serial.txNumber(m_lightSensors.MAX_VALUE); m_serial << detail::ENDL; } static void printVersion() { m_serial << "AdaptiveBrightness v" << detail::VERSION << detail::ENDL; } static void printUnknown() { m_serial << "Unknown command \""; for(uint16_t i = 0; i < m_inputSize; ++i) { m_serial << static_cast(m_inputBuffer[i]); } m_serial << "\"" << detail::ENDL; } }; template char Terminal::m_inputBuffer[INPUT_BUFFER_SIZE]; template uint16_t Terminal::m_inputSize = 0;