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

Eric Duminil's avatar
Eric Duminil committed
3
namespace sensor_console {
4
  const uint8_t MAX_COMMANDS = 26;
Eric Duminil's avatar
Todo    
Eric Duminil committed
5
  //TODO: Check if it's really needed? Is it including parameter???
6
  const uint8_t MAX_COMMAND_SIZE = 40;
Eric Duminil's avatar
Eric Duminil committed
7

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

10
11
12
13
14
15
  enum input_type {
    NONE,
    INT32,
    STRING
  };

Eric Duminil's avatar
Eric Duminil committed
16
  struct Command {
Eric Duminil's avatar
Eric Duminil committed
17
    const char *name;
Eric Duminil's avatar
Eric Duminil committed
18
    union {
19
      void (*voidFunction)();
Eric Duminil's avatar
Eric Duminil committed
20
      void (*intFunction)(int32_t);
21
      void (*strFunction)(char*);
Eric Duminil's avatar
Eric Duminil committed
22
    };
23
    const char *doc;
24
25
26
27
28
29
30
31
    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];
32
33
  };

Eric Duminil's avatar
Eric Duminil committed
34
  Command commands[MAX_COMMANDS];
35

36
  bool addCommand(const char *name, const __FlashStringHelper *doc_fstring) {
Eric Duminil's avatar
Eric Duminil committed
37
38
    if (commands_count < MAX_COMMANDS) {
      commands[commands_count].name = name;
39
40
      commands[commands_count].doc = (const char*) doc_fstring;
      return true;
Eric Duminil's avatar
Eric Duminil committed
41
    } else {
Eric Duminil's avatar
Eric Duminil committed
42
      Serial.println(F("Too many commands have been defined."));
43
44
45
46
47
48
49
50
      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;
Eric Duminil's avatar
Eric Duminil committed
51
    }
52
53
  }

54
  void defineIntCommand(const char *name, void (*function)(int32_t), const __FlashStringHelper *doc_fstring) {
55
    if (addCommand(name, doc_fstring)) {
Eric Duminil's avatar
Eric Duminil committed
56
      commands[commands_count].intFunction = function;
57
58
59
60
61
62
63
64
      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;
65
    }
66
67
  }

Eric Duminil's avatar
Eric Duminil committed
68
  /*
69
70
   * Tries to split a string command (e.g. 'mqtt 60' or 'show_csv') into
   * a CommandLine struct (function_name, argument_type and argument)
Eric Duminil's avatar
Eric Duminil committed
71
   */
72
73
  void parseCommand(const char *command, CommandLine &command_line) {
    if (strlen(command) == 0) {
Eric Duminil's avatar
Eric Duminil committed
74
      Serial.println(F("Received empty command"));
75
76
      command_line.argument_type = NONE;
      return;
Eric Duminil's avatar
Eric Duminil committed
77
    }
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95

    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;
Eric Duminil's avatar
Eric Duminil committed
96
    } else {
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
      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);
112
      Serial.print(F(" "));
113
114
      Serial.print(commands[i].doc);
      Serial.println(F("."));
Eric Duminil's avatar
Eric Duminil committed
115
    }
Eric Duminil's avatar
Eric Duminil committed
116
117
  }

Eric Duminil's avatar
Doc    
Eric Duminil committed
118
119
120
121
  /*
   * 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
122
  void processSerialInput(const byte input_byte) {
123
124
125
126
    static char input_line[MAX_COMMAND_SIZE];
    static unsigned int input_pos = 0;
    switch (input_byte) {
    case '\n': // end of text
127
      Serial.println();
128
      input_line[input_pos] = 0;
129
      execute(input_line);
130
131
132
133
      input_pos = 0;
      break;
    case '\r': // discard carriage return
      break;
Eric Duminil's avatar
Eric Duminil committed
134
135
136
    case '\b': // backspace
      if (input_pos > 0) {
        input_pos--;
137
        Serial.print(F("\b \b"));
Eric Duminil's avatar
Eric Duminil committed
138
139
      }
      break;
140
    default:
141
142
143
      if (input_pos == 0) {
        Serial.print(F("> "));
      }
144
      // keep adding if not full ... allow for terminating null byte
Eric Duminil's avatar
Eric Duminil committed
145
      if (input_pos < (MAX_COMMAND_SIZE - 1)) {
146
        input_line[input_pos++] = input_byte;
147
        Serial.print((char) input_byte);
Eric Duminil's avatar
Eric Duminil committed
148
      }
149
150
151
152
      break;
    }
  }

Eric Duminil's avatar
Eric Duminil committed
153
154
155
156
157
158
  void checkSerialInput() {
    while (Serial.available() > 0) {
      sensor_console::processSerialInput(Serial.read());
    }
  }

Eric Duminil's avatar
Eric Duminil committed
159
  /*
160
   * Tries to find the corresponding callback for a given command. Name and parameter type should fit.
Eric Duminil's avatar
Eric Duminil committed
161
   */
162
163
164
  void execute(const char *command_str) {
    CommandLine input;
    parseCommand(command_str, input);
Eric Duminil's avatar
Eric Duminil committed
165
    for (uint8_t i = 0; i < commands_count; i++) {
166
      if (!strcmp(input.function_name, commands[i].name) && input.argument_type == commands[i].parameter_type) {
Eric Duminil's avatar
Eric Duminil committed
167
        Serial.print(F("Calling : "));
168
169
170
        Serial.print(input.function_name);
        switch (input.argument_type) {
        case NONE:
Eric Duminil's avatar
Eric Duminil committed
171
          Serial.println(F("()"));
Eric Duminil's avatar
Eric Duminil committed
172
          commands[i].voidFunction();
173
174
175
176
177
178
179
180
181
182
183
184
185
          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;
Eric Duminil's avatar
Eric Duminil committed
186
        }
Eric Duminil's avatar
Eric Duminil committed
187
      }
188
    }
Eric Duminil's avatar
Eric Duminil committed
189
    Serial.print(F("'"));
190
    Serial.print(command_str);
Eric Duminil's avatar
Eric Duminil committed
191
    Serial.println(F("' not supported. Available commands :"));
Eric Duminil's avatar
Eric Duminil committed
192
    listAvailableCommands();
193
  }
194

195
}