#include "sensor_console.h" namespace sensor_console { const uint8_t MAX_COMMANDS = 26; //TODO: Check if it's really needed? Is it including parameter??? const uint8_t MAX_COMMAND_SIZE = 40; uint8_t commands_count = 0; enum input_type { NONE, INT32, STRING }; struct Command { const char *name; union { void (*voidFunction)(); void (*intFunction)(int32_t); void (*strFunction)(char*); }; const char *doc; input_type parameter_type; }; struct CommandLine { char function_name[MAX_COMMAND_SIZE]; input_type argument_type; int32_t int_argument; char str_argument[MAX_COMMAND_SIZE]; }; Command commands[MAX_COMMANDS]; bool addCommand(const char *name, const __FlashStringHelper *doc_fstring) { if (commands_count < MAX_COMMANDS) { commands[commands_count].name = name; commands[commands_count].doc = (const char*) doc_fstring; return true; } else { Serial.println(F("Too many commands have been defined.")); return false; } } void defineCommand(const char *name, void (*function)(), const __FlashStringHelper *doc_fstring) { if (addCommand(name, doc_fstring)) { commands[commands_count].voidFunction = function; commands[commands_count++].parameter_type = NONE; } } void defineIntCommand(const char *name, void (*function)(int32_t), const __FlashStringHelper *doc_fstring) { if (addCommand(name, doc_fstring)) { commands[commands_count].intFunction = function; commands[commands_count++].parameter_type = INT32; } } void defineStringCommand(const char *name, void (*function)(char*), const __FlashStringHelper *doc_fstring) { if (addCommand(name, doc_fstring)) { commands[commands_count].strFunction = function; commands[commands_count++].parameter_type = STRING; } } /* * Tries to split a string command (e.g. 'mqtt 60' or 'show_csv') into * a CommandLine struct (function_name, argument_type and argument) */ void parseCommand(const char *command, CommandLine &command_line) { if (strlen(command) == 0) { Serial.println(F("Received empty command")); command_line.argument_type = NONE; return; } char *first_space; first_space = strchr(command, ' '); if (first_space == NULL) { command_line.argument_type = NONE; strlcpy(command_line.function_name, command, MAX_COMMAND_SIZE); return; } strlcpy(command_line.function_name, command, first_space - command + 1); strlcpy(command_line.str_argument, first_space + 1, MAX_COMMAND_SIZE - (first_space - command) - 1); char *end; command_line.int_argument = strtol(command_line.str_argument, &end, 0); // Accepts 123 or 0xFF00FF if (*end) { command_line.argument_type = STRING; } else { command_line.argument_type = INT32; } } int compareCommandNames(const void *s1, const void *s2) { struct Command *c1 = (struct Command*) s1; struct Command *c2 = (struct Command*) s2; return strcmp(c1->name, c2->name); } void listAvailableCommands() { qsort(commands, commands_count, sizeof(commands[0]), compareCommandNames); for (uint8_t i = 0; i < commands_count; i++) { Serial.print(F(" ")); Serial.print(commands[i].name); Serial.print(F(" ")); Serial.print(commands[i].doc); Serial.println(F(".")); } } /* * Saves bytes from Serial.read() until enter is pressed, and tries to run the corresponding command. * http://www.gammon.com.au/serial */ void processSerialInput(const byte input_byte) { static char input_line[MAX_COMMAND_SIZE]; static unsigned int input_pos = 0; switch (input_byte) { case '\n': // end of text Serial.println(); input_line[input_pos] = 0; execute(input_line); input_pos = 0; break; case '\r': // discard carriage return break; case '\b': // backspace if (input_pos > 0) { input_pos--; Serial.print(F("\b \b")); } break; default: if (input_pos == 0) { Serial.print(F("> ")); } // keep adding if not full ... allow for terminating null byte if (input_pos < (MAX_COMMAND_SIZE - 1)) { input_line[input_pos++] = input_byte; Serial.print((char) input_byte); } break; } } void checkSerialInput() { while (Serial.available() > 0) { sensor_console::processSerialInput(Serial.read()); } } /* * Tries to find the corresponding callback for a given command. Name and parameter type should fit. */ void execute(const char *command_str) { CommandLine input; parseCommand(command_str, input); for (uint8_t i = 0; i < commands_count; i++) { if (!strcmp(input.function_name, commands[i].name) && input.argument_type == commands[i].parameter_type) { Serial.print(F("Calling : ")); Serial.print(input.function_name); switch (input.argument_type) { case NONE: Serial.println(F("()")); commands[i].voidFunction(); return; case INT32: Serial.print(F("(")); Serial.print(input.int_argument); Serial.println(F(")")); commands[i].intFunction(input.int_argument); return; case STRING: Serial.print(F("('")); Serial.print(input.str_argument); Serial.println(F("')")); commands[i].strFunction(input.str_argument); return; } } } Serial.print(F("'")); Serial.print(command_str); Serial.println(F("' not supported. Available commands :")); listAvailableCommands(); } }