sensor_console.cpp 4.61 KB
Newer Older
1
#include "sensor_console.h"
2

Eric Duminil's avatar
Eric Duminil committed
3
namespace sensor_console {
Eric Duminil's avatar
Eric Duminil committed
4
  const uint8_t MAX_COMMANDS = 20;
Eric Duminil's avatar
Eric Duminil committed
5
  const uint8_t MAX_COMMAND_SIZE = 30;
Eric Duminil's avatar
Eric Duminil committed
6

Eric Duminil's avatar
Eric Duminil committed
7
  uint8_t commands_count = 0;
Eric Duminil's avatar
Eric Duminil committed
8

Eric Duminil's avatar
Eric Duminil committed
9
  struct Command {
Eric Duminil's avatar
Eric Duminil committed
10
    const char *name;
Eric Duminil's avatar
Eric Duminil committed
11
12
13
14
    union {
      void (*intFunction)(int32_t);
      void (*voidFunction)(void);
    };
15
    const char *doc;
Eric Duminil's avatar
Eric Duminil committed
16
    bool has_parameter;
17
18
  };

Eric Duminil's avatar
Eric Duminil committed
19
  Command commands[MAX_COMMANDS];
20

Eric Duminil's avatar
Eric Duminil committed
21
  //NOTE: Probably possible to DRY (with templates?)
Eric Duminil's avatar
Eric Duminil committed
22
  void defineCommand(const char *name, void (*function)(void), const char *doc) {
Eric Duminil's avatar
Eric Duminil committed
23
24
25
26
27
28
    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++;
Eric Duminil's avatar
Eric Duminil committed
29
    } else {
Eric Duminil's avatar
Eric Duminil committed
30
      Serial.println(F("Too many commands have been defined."));
Eric Duminil's avatar
Eric Duminil committed
31
    }
32
33
  }

Eric Duminil's avatar
Eric Duminil committed
34
  void defineIntCommand(const char *name, void (*function)(int32_t), const char *doc) {
Eric Duminil's avatar
Eric Duminil committed
35
36
37
38
39
40
    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++;
41
    } else {
Eric Duminil's avatar
Eric Duminil committed
42
      Serial.println(F("Too many commands have been defined."));
43
    }
44
45
  }

Eric Duminil's avatar
Eric Duminil committed
46
47
48
49
  /*
   * 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.
   */
Eric Duminil's avatar
Eric Duminil committed
50
  uint8_t parseCommand(const char *command, char *function_name, int32_t &argument) {
Eric Duminil's avatar
Eric Duminil committed
51
52
    char split_command[MAX_COMMAND_SIZE];
    strlcpy(split_command, command, MAX_COMMAND_SIZE);
Eric Duminil's avatar
Eric Duminil committed
53
    char *arg;
Eric Duminil's avatar
Eric Duminil committed
54
55
56
    char *part1;
    part1 = strtok(split_command, " ");
    if (!part1) {
Eric Duminil's avatar
Eric Duminil committed
57
      Serial.println(F("Received empty command"));
Eric Duminil's avatar
Eric Duminil committed
58
      // Empty string
Eric Duminil's avatar
Eric Duminil committed
59
      return 1;
Eric Duminil's avatar
Eric Duminil committed
60
    }
Eric Duminil's avatar
Eric Duminil committed
61
    strlcpy(function_name, part1, MAX_COMMAND_SIZE);
Eric Duminil's avatar
Eric Duminil committed
62
    arg = strtok(NULL, " ");
Eric Duminil's avatar
Eric Duminil committed
63
    uint8_t code = 0;
Eric Duminil's avatar
Eric Duminil committed
64
65
    if (arg) {
      char *end;
Eric Duminil's avatar
Eric Duminil committed
66
      argument = strtol(arg, &end, 10);
Eric Duminil's avatar
Eric Duminil committed
67
68
      if (*end) {
        // Second argument isn't a number
Eric Duminil's avatar
Eric Duminil committed
69
        code = 2;
Eric Duminil's avatar
Eric Duminil committed
70
71
      }
    } else {
Eric Duminil's avatar
Eric Duminil committed
72
      // No argument
Eric Duminil's avatar
Eric Duminil committed
73
      code = 2;
Eric Duminil's avatar
Eric Duminil committed
74
    }
Eric Duminil's avatar
Eric Duminil committed
75
    return code;
Eric Duminil's avatar
Eric Duminil committed
76
77
  }

Eric Duminil's avatar
Doc    
Eric Duminil committed
78
79
80
81
  /*
   * Saves bytes from Serial.read() until enter is pressed, and tries to run the corresponding command.
   *   http://www.gammon.com.au/serial
   */
Eric Duminil's avatar
Eric Duminil committed
82
  void processSerialInput(const byte input_byte) {
83
84
85
86
    static char input_line[MAX_COMMAND_SIZE];
    static unsigned int input_pos = 0;
    switch (input_byte) {
    case '\n': // end of text
87
      Serial.println();
88
      input_line[input_pos] = 0;
Eric Duminil's avatar
Eric Duminil committed
89
      runCommand(input_line);
90
91
92
93
      input_pos = 0;
      break;
    case '\r': // discard carriage return
      break;
Eric Duminil's avatar
Eric Duminil committed
94
95
96
    case '\b': // backspace
      if (input_pos > 0) {
        input_pos--;
97
        Serial.print(F("\b \b"));
Eric Duminil's avatar
Eric Duminil committed
98
99
      }
      break;
100
    default:
101
102
103
      if (input_pos == 0) {
        Serial.print(F("> "));
      }
104
      // keep adding if not full ... allow for terminating null byte
Eric Duminil's avatar
Eric Duminil committed
105
      if (input_pos < (MAX_COMMAND_SIZE - 1)) {
106
        input_line[input_pos++] = input_byte;
107
        Serial.print((char) input_byte);
Eric Duminil's avatar
Eric Duminil committed
108
      }
109
110
111
112
      break;
    }
  }

Eric Duminil's avatar
Eric Duminil committed
113
114
115
116
117
118
119
120
121
  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++) {
Eric Duminil's avatar
Eric Duminil committed
122
      Serial.print("  ");
Eric Duminil's avatar
Eric Duminil committed
123
124
      Serial.print(commands[i].name);
      Serial.print(commands[i].doc);
125
      Serial.println(".");
Eric Duminil's avatar
Eric Duminil committed
126
    }
Eric Duminil's avatar
Eric Duminil committed
127
    led_effects::showKITTWheel(color::red, 1);
Eric Duminil's avatar
Eric Duminil committed
128
129
  }

Eric Duminil's avatar
Eric Duminil committed
130
131
132
  /*
   * Tries to find the corresponding callback for a given command. Name and number of argument should fit.
   */
Eric Duminil's avatar
Eric Duminil committed
133
  void runCommand(const char *command) {
Eric Duminil's avatar
Eric Duminil committed
134
    char function_name[MAX_COMMAND_SIZE];
Eric Duminil's avatar
Eric Duminil committed
135
136
137
    int32_t argument = 0;
    bool has_argument;
    has_argument = (parseCommand(command, function_name, argument) == 0);
Eric Duminil's avatar
Eric Duminil committed
138

Eric Duminil's avatar
Eric Duminil committed
139
140
141
    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 : "));
Eric Duminil's avatar
Eric Duminil committed
142
        Serial.print(function_name);
Eric Duminil's avatar
Eric Duminil committed
143
        if (has_argument) {
Eric Duminil's avatar
Eric Duminil committed
144
          Serial.print(F("("));
Eric Duminil's avatar
Eric Duminil committed
145
          Serial.print(argument);
Eric Duminil's avatar
Eric Duminil committed
146
          Serial.println(F(")"));
Eric Duminil's avatar
Eric Duminil committed
147
          commands[i].intFunction(argument);
Eric Duminil's avatar
Eric Duminil committed
148
        } else {
Eric Duminil's avatar
Eric Duminil committed
149
          Serial.println(F("()"));
Eric Duminil's avatar
Eric Duminil committed
150
          commands[i].voidFunction();
Eric Duminil's avatar
Eric Duminil committed
151
        }
Eric Duminil's avatar
Eric Duminil committed
152
        return;
Eric Duminil's avatar
Eric Duminil committed
153
      }
154
    }
Eric Duminil's avatar
Eric Duminil committed
155
156
157
    Serial.print(F("'"));
    Serial.print(command);
    Serial.println(F("' not supported. Available commands :"));
Eric Duminil's avatar
Eric Duminil committed
158
    listAvailableCommands();
159
160
  }
}