AdaptiveBrightness/AdaptiveBrightnessFirmware/Inc/terminal.hpp

203 lines
6.0 KiB
C++
Raw Normal View History

2020-06-29 00:33:14 +02:00
#pragma once
#include <cctype>
#include <cstdint>
#include <cstdlib>
#include <cstring>
#include "light_sensors.hpp"
2020-06-29 00:33:14 +02:00
namespace detail {
constexpr auto ENDL = "\r\n";
constexpr auto HELP_CMD = "help";
constexpr auto READ_CMD = "read";
2020-07-03 22:34:10 +02:00
constexpr auto READ_CSV_CMD = "read csv";
2020-07-03 22:40:35 +02:00
constexpr auto RANGE_CMD = "range";
2020-06-29 00:33:14 +02:00
constexpr auto VERSION_CMD = "version";
2020-07-03 22:40:35 +02:00
constexpr auto VERSION = "1.2";
2020-06-29 00:33:14 +02:00
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 Uart>
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();
2020-06-29 00:33:14 +02:00
}
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;
2020-06-29 00:33:14 +02:00
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<char>(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();
2020-06-29 00:33:14 +02:00
}
2020-07-03 22:34:10 +02:00
else if(detail::stringEquals(m_inputBuffer, detail::READ_CSV_CMD, m_inputSize)) {
readSensorsCsv();
}
2020-07-03 22:40:35 +02:00
else if(detail::stringEquals(m_inputBuffer, detail::RANGE_CMD, m_inputSize)) {
getSensorsRange();
}
2020-06-29 00:33:14 +02:00
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;
2020-07-03 22:34:10 +02:00
m_serial << detail::READ_CSV_CMD << " ...: read LDR values as csv" << detail::ENDL;
2020-07-03 22:40:35 +02:00
m_serial << detail::RANGE_CMD << " ......: print LDR sensor range" << detail::ENDL;
2020-06-29 00:33:14 +02:00
m_serial << detail::VERSION_CMD << " ....: displays firmware version" << detail::ENDL;
}
static void readSensors()
{
const auto sensorValues = m_lightSensors.getValues();
2020-07-03 18:58:54 +02:00
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<uint16_t, 10, 4, ' '>(sensorValues[i]);
m_serial << " - ";
m_serial.template txNumber<uint8_t, 10, 3, ' '>(sensorValues[i] * 100 / m_lightSensors.MAX_VALUE);
m_serial << "%" << detail::ENDL;
}
}
2020-06-29 00:33:14 +02:00
2020-07-03 22:34:10 +02:00
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;
}
2020-07-03 22:40:35 +02:00
static void getSensorsRange()
{
m_serial << "0,";
m_serial.txNumber(m_lightSensors.MAX_VALUE);
m_serial << detail::ENDL;
}
2020-06-29 00:33:14 +02:00
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<char>(m_inputBuffer[i]);
}
m_serial << "\"" << detail::ENDL;
}
};
template<class Uart>
char Terminal<Uart>::m_inputBuffer[INPUT_BUFFER_SIZE];
template<class Uart>
uint16_t Terminal<Uart>::m_inputSize = 0;