#include "sensor_console.h" namespace sensor_console { const uint8_t MAX_COMMANDS = 20; const uint8_t MAX_COMMAND_SIZE = 30; uint8_t commands_count = 0; struct Command { const char *name; union { void (*intFunction)(int32_t); void (*voidFunction)(void); }; const char *doc; bool has_parameter; }; Command commands[MAX_COMMANDS]; //NOTE: Probably possible to DRY (with templates?) void defineCommand(const char *name, void (*function)(void), const char *doc) { if (commands_count < MAX_COMMANDS) { commands[commands_count].name = name; commands[commands_count].voidFunction = function; commands[commands_count].doc = doc; commands[commands_count].has_parameter = false; commands_count++; } else { Serial.println(F("Too many commands have been defined.")); } } void defineIntCommand(const char *name, void (*function)(int32_t), const char *doc) { if (commands_count < MAX_COMMANDS) { commands[commands_count].name = name; commands[commands_count].intFunction = function; commands[commands_count].doc = doc; commands[commands_count].has_parameter = true; commands_count++; } else { Serial.println(F("Too many commands have been defined.")); } } /* * Tries to split a string command (e.g. 'mqtt 60' or 'show_csv') into a function_name and an argument. * Returns 0 if both are found, 1 if there is a problem and 2 if no argument is found. */ uint8_t parseCommand(const char *command, char *function_name, int32_t &argument) { char split_command[MAX_COMMAND_SIZE]; strlcpy(split_command, command, MAX_COMMAND_SIZE); Serial.print(F("Received : '")); Serial.print(command); Serial.println("'"); char *arg; char *part1; part1 = strtok(split_command, " "); if (!part1) { // Empty string return 1; } strlcpy(function_name, part1, MAX_COMMAND_SIZE); arg = strtok(NULL, " "); uint8_t code = 0; if (arg) { char *end; argument = strtol(arg, &end, 10); if (*end) { // Second argument isn't a number code = 2; } } else { // No argument code = 2; } return code; } /* * 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 input_line[input_pos] = 0; runCommand(input_line); input_pos = 0; break; case '\r': // discard carriage return break; case '\b': // backspace if (input_pos > 0) { input_pos--; } break; default: // keep adding if not full ... allow for terminating null byte if (input_pos < (MAX_COMMAND_SIZE - 1)) { input_line[input_pos++] = input_byte; } break; } } int compareName(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]), compareName); for (uint8_t i = 0; i < commands_count; i++) { Serial.print(" "); Serial.print(commands[i].name); Serial.print(commands[i].doc); Serial.println("."); } led_effects::showKITTWheel(color::red, 1); } /* * Tries to find the corresponding callback for a given command. Name and number of argument should fit. */ void runCommand(const char *command) { char function_name[MAX_COMMAND_SIZE]; int32_t argument = 0; bool has_argument; has_argument = (parseCommand(command, function_name, argument) == 0); for (uint8_t i = 0; i < commands_count; i++) { if (!strcmp(function_name, commands[i].name) && has_argument == commands[i].has_parameter) { Serial.print(F("Calling : ")); Serial.print(function_name); if (has_argument) { Serial.print("("); Serial.print(argument); Serial.println(")"); commands[i].intFunction(argument); } else { Serial.println("()"); commands[i].voidFunction(); } return; } } Serial.println(F("Message not supported. Available commands :")); listAvailableCommands(); } }